Start 4 All Phase 1 - Statistical Analysis Plan

v1.4 (International)

Author

Marc Henrion

Published

May 27, 2025

1 List of abbreviations

Table 1 lists all abbreviations used throughout this document.

Table 1: List of abbreviations
Abbreviation Explanation
ACF Active case finding: ACF is the process of actively identifying people with tuberculosis (TB). ACF aims to identify people with TB either in those who do not recognize that they have symptoms, or those who do recognize symptoms but for whatever reason do not, or cannot, access services at health-care facilities, or those who are asymptomatic.
CAD Computer-aided detection: CAD software algorithms that use deep learning approaches to classify the likelihood of disease. For this study this will be pulmonary TB based on analysis of a chest X-ray image (CXR). CAD is recommended by the World Health Organization (WHO) as an alternative to human interpretation of CXR for people aged 15 years or older.
CRP C-Reactive Protein: CRP is a blood inflammatory marker that is elevated in the presence of an infection, inflammatory or neoplastic (cancer) conditions, including TB. WHO recommends CRP as a screening tool for TB among people living with HIV (PLHIV).
CXR Chest x-ray
DYT Diagnostic yield per test
GA Gastric aspirate
ICF Intensified case finding: ICF is an activity, recommended by the WHO, intended to detect possible TB cases among people attending health facilities for reasons not associated with TB.). For adults, in Start4All, this will be defined as ambulatory people attending outpatient clinics. For children, this will also be defined as ambulatory children attending outpatient clinics but inpatients may be recruited if recruitment numbers or TB prevalence are lower than expected.
IDP Internally displaced persons: IDP are people living in either camps or host communities as a result of forced migration within a countries border.
LAM Lipoarabinomannan: LAM is a glycolipid, and a virulence factor associated with Mycobacterium tuberculosis, the bacterium responsible for TB. LAM is excreted in urine, where it can be detected using rapid lateral flow tests.
MCMC Markov Chain Monte Carlo
NPA Nasopharyngeal aspiration
NPV Negative predictive value
PCF Passive case finding: PCF is a modality of case finding in which people with TB symptoms present voluntarily at health care facilities. PCF is the most common approach used by TB diagnostic services.
PLHIV People / person living with HIV
POC Point-of-care
PPV Positive predictive value
RDT Rapid diagnostic test
SAP Statistical analysis plan
SSM Sputum smear microscopy
TB tuberculosis

2 Protocol version

This Statistical Analysis Plan (SAP) v1.4 is based on the Start 4 All International Protocol v2.0, dated 12 June 2024.

2.1 Version history

Table 2: Version history of the SAP since v1.0.
Version Date Author Edits
1.4 2025-05-27 Marc Henrion Slight changes to protocol summary text; revised language for PCF/ICF (facility-based) ACF (community-based) and pooled (aggregate) analysis; addition of mock-ups for community-based CXR-CAD entry to study
1.3 2025-05-27 Marc Henrion All analyses tested; mock-up output for PCF/ICF setting included
1.2 2024-05-27 Marc Henrion Clarified agorithms with pooled Xpert Ultra testing and updated JAGS files correspondingly
1.1 2025-05-26 Marc Henrion Descriptive analysis mock-ups added; full JAGS model files added; details of MCMC run parameters; added JAGS version number; slightly revised secondary analyses
1.0 2025-05-25 Marc Henrion First full version of the SAP (while development versions 0.1 to 0.14 exist, no version history record was kept prior to v1.0)

3 Protocol summary

Start 4 All is a four-year cross-disciplinary global tuberculosis diagnostic research programme funded by Unitaid, led by the Liverpool School of Tropical Medicine working with StopTB Partnership and partners in Bangladesh, Brazil, Cameroon, Kenya, Malawi, Nigeria and Viet Nam.

Start 4 All aims to develop and evaluate novel combinations of tuberculosis diagnostics that are deliverable within the primary healthcare setting to increase the number of people diagnosed with tuberculosis and to ultimately improve linkage to treatment. Start4All is split into three distinct periods.

  1. Phase 1 (12 months) - Within all 7 countries, cross-sectional surveys to generate data on diagnostic assay performances in key, vulnerable and underserved populations.

  2. Stagegate (6 months) - Utilising data generated in Phase 1, a statistical and economical model will be developed to predict the optimal diagnostic test combination for both facility and community based screening.

  3. Phase 2 (18 months) - a large, multi-country implementation study to evaluate the chosen optimal combination(s) in different settings. The exact study design is not yet confirmed.

This SAP focuses on Phase 1, with a separate SAP being written for the trial in Phase 2. There will also be a separate analysis plan for the economic evaluation of the diagnostic test combinations and a separate SAP for the paediatric data analysis.

3.1 Study design

Phase 1 consists of multiple cross-sectional surveys in 7 countries and in different general and marginalised populations within those countries.

Table 3: Summary of study populations, countries, and procedures.
Population Currently implemented TB screening / diagnosis approach Local laboratory Country X-ray availability Case finding approach % with TB among those testes Additional diagnostic tests and test combinations evaluated as part of Start 4 All
Primary health care attenders Sympton screen; sputum smear microscopy; sputum transport for Xpert Basic / Xpert Ultra Bangladesh, Brazil, Cameroon, Kenya, Malawi, Nigeria, Viet Nam No PCF / ICF 5-15% CAD CXR, POC CRP, CRP RDT, LAM, pooling Ultra
District / secondary hospital attenders Symptom screen; sputum Xper testing (where available); 1st gen LAM for HIV positive admissions (where avilable) Medium / Xpert Ultra Bangladesh, Cameroon, Kenya, Malawi, Nigeria, Viet Nam Yes PCF / ICF 10-20% CAD CXR, POC CRP, CRP RDT, LAM, pooling Ultra
Key / marginalised populations (informal settlements, IPDs, rural poor, pastoralists) Symptom screen; mobile chest X-ray (predominantly human interpretation); sputum transport for smear/Xpert/culture Xpert Ultra only Bangladesh, Cameroon, Nigeria Limited / mobile trucks PCF / ICF / ACF 0.5-10% CAD CXR, POC CRP, CRP RDT, LAM, Pooling Ultra
Children Symptom screen; Xpert testing (stool, NPA, GA, where available); X-ray (human interpretation); IGRA (Viet Nam) Medium / Xpert Ultra Cameroon, Viet Nam Yes (if in hospital) PCF / ICF / ACF 8-12% CAD CXR, POC CRP, CRP RDT, LAM, pooling Ultra

Table 3 summarises the different surveys. This study design was selected because (together with diagnostic clinical trials) surveys are considered the best design for the evaluation of diagnostics. We will evaluate the performance of quantitative and semiqualitative CRP, computer aided detection chest x-ray (CAD CXR), the pooling of sputum from four individuals into one test cartridge and the FujiLAMTM 3rd generation urine lipoarabinomannan (LAM) as screening tests for pulmonary TB.

CRP, LAM and CAD CXR are conditionally recommended for use in TB screening, whether that be for those living with HIV (CRP and LAM), or in the absence of human interpreters (for CAD-CXR) but data in other key, vulnerable and under-served groups or the general population is limited. Pooling of sputum as a cost saving strategy is currently being reviewed by the WHO and it is anticipated that Phase 1 data from Start 4 All will be contribute to this process.

A composite microbiological reference standard will be used to describe the sensitivity, specificity and positive and negative predictive value of different assays and their combinations. The composite standard will classify participants as ‘microbiologically confirmed’, if their sputum culture or Xpert Ultra (as a single test) are positive or as ‘unlikely TB’ if they have negative culture AND negative Xpert Ultra results.

3.1.1 Case definitions

Participants will be classified as:

  • Microbiologically confirmed TB case: Participants with a positive microbiological reference standard (MRS) test of MGIT culture (Bangladesh, Cameroon, Kenya, Malawi, Nigeria and Viet Nam) or solid culture (Brazil only). Where culture results are not available, a positive Xpert Ultra and/or smear microscopy will be used.

  • Clinically diagnosed TB case: Participants in whom a healthcare professional has decided to initiate TB treatment based on clinical symptoms and/or signs and/or human-interpreted CXR consistent with TB and in the absence of a positive MRS

  • Unlikely TB case: Participants who are neither a microbiologically confirmed TB case nor a clinically diagnosed TB case.

For the purpose of the performance evaluation, we will use microbiological confirmation as the reference.

Table 4 summarises the screening assays evaluated in this study.

Table 4: Summary of screening and diagnostic assays evaluated in this study.
Assay type Assay
Point-of-care C-Reactive Protein (quantitative) CRP POC
Point-of-care, rapid diagnostic test C-Reactive Protein (semi-quantitative) CRP RDT
Computer-aided detection from digital chest X-ray Digital chest X-ray + Qure.ai
Urine Lateral Flow Test FujiLAM^TM^
Molecular diagnostic Xpert Ultra
Molecular diagnostic Pooled Xpert Ultra

3.2 Study Endpoints

3.2.1 Primary endpoint

Generation of robust estimates of the diagnostic accuracy and performance of TB diagnostic tests and test combinations in primary healthcare settings and key and vulnerable populations.

3.2.2 Secondary study endpoints

  • Estimates of the predicted performance of TB diagnostic test combinations and how they perform in specific populations.

  • Selection of the optimal TB diagnostic test combinations for implementation and scale-up in primary healthcare settings and key and vulnerable populations in terms of their cost-efficiency, feasibility, and modelled accuracy.

3.3 Sample size

Diagnostic test combinations for the Phase 1 cross-sectional studies are tailored by country and key populations. We envisage conducting one cross sectional study among pastoralists, one study among IDPs, one among people living in informal settlements, one among the rural poor, and 6 studies among populations in clinical facilities (total of 18 cross sectional studies across 6 countries). Each study will comprise about 600 participants, including a minimum of 100 bacteriologically confirmed participants in most populations. The key populations and number of studies for Phase 1 studies are shown below.

The sample sizes were chosen to comply with the sample size criterion required for WHO guideline development and to achieve an acceptable level of precision for the estimates of the performance of the diagnostic tests. The sample sizes are also suitable for estimating costs and cost-efficiency of the TB diagnostic combinations.

Table 5 gives the number of TB cases / number of subjects at different prevalence values for TB and different values of precision for 95% CI of point estimate of number of bacteriologically confirmed TB cases identified from each sample population. Populations with low proportions (<10%) of bacteriologically confirmed TB (children, community-based in Viet Nam), will use the same cross-sectional design, but the number of bacteriologically confirmed participants will be purposely enriched by inviting participants attending adjacent centers to the study centers. Once included in the study, participants will undergo all tests in the diagnostic combination.

The calculations in tbl-sampSizeCalc have been obtained using the standard Wald normal approximation:

\[ n_{unadj}=p\cdot(1-p)\cdot\left(\frac{z_{\alpha/2}}{E}\right)^2 \] where \(p\) = target proportion (sensitivity in your case; \(p=0.9\)), E = target precision (or margin-of-error) and \(z_{\alpha/2}\) is the (two-sided) critical value from the normal distribution for significance level \(\alpha\) (in our case \(\alpha=0.05\) and \(z_{alpha/2}=1.96\)).

Code
sampSize_OnePropCI<-function(p_hat,E=NULL,n=NULL,alpha=0.05,method="WaldNormalApprox"){
  # p_hat = expected proportion
  # E = desired error of margin (half width of the confidence interval); if specified then n gets computed
  # n = fixed sample size; if specified then E gets computed
  # alpha = 1 - confidence level
  # method = one of "WaldNormalApprox" or "WilsonScoreApprox"
  # NOTE: exactly one of n, E needs to be specified -- they cannot both be set to NULL or both be specified
  
  z<-qnorm(1-alpha/2)
  
  if(is.null(n) & !is.null(E)){
    if(method=="WaldNormalApprox"){
      n<-p_hat*(1-p_hat)*(z/E)^2
    }else if(method=="WilsonScoreApprox"){
      a<-1
      b<-(2-p_hat*(1-p_hat)/E^2)*z^2
      c<-(4*E^2-1)*(z^4)/(4*E^2)
      n<-(-b + sqrt(b^2-4*a*c))/(2*a)
    }else{stop("The method parameter needs to be one of WaldNormalApprox or WilsonScoreApprox")}
    
    return(n)
  }else if (is.null(E) & !is.null(n)){
    if(method=="WilsonScoreApprox"){warning("Argument method is set to WilsonScoreApprox but this will be ignored and WaldNormalApprox be used instead.")}
    E<-sqrt((p_hat*(1-p_hat)*z^2)/n)
    
    return(E)
  }else{
    stop("E and n cannot both be NULL or both be specified - you need to specify one of these two and leave the other unspecified / NULL.")
  }
  
}

dfSS<-data.frame(
  tbPrevalence=c(1:5,8:10,12,15,20,27)/100,
  prec5=NA,
  prec6=NA,
  prec7=NA,
  prec8=NA,
  prec10=NA,
  prec15=NA
) %>%
  mutate(
    prec5=paste(sep=" / ",ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.05)),ceiling(c(ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.05))/tbPrevalence))),
    prec6=paste(sep=" / ",ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.06)),ceiling(c(ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.06))/tbPrevalence))),
    prec7=paste(sep=" / ",ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.07)),ceiling(c(ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.07))/tbPrevalence))),
    prec8=paste(sep=" / ",ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.08)),ceiling(c(ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.08))/tbPrevalence))),
    prec10=paste(sep=" / ",ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.1)),ceiling(c(ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.1))/tbPrevalence))),
    prec15=paste(sep=" / ",ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.15)),ceiling(c(ceiling(sampSize_OnePropCI(p_hat=0.9,E=0.15))/tbPrevalence)))
  ) %>%
  mutate(tbPrevalence=paste(sep="",100*tbPrevalence,"%"))

dfSS %>%
  kable(row.names=FALSE,col.names=c("","5%","6%","7%","8%","10%","15%")) %>%
  kable_styling(full_width=FALSE) %>%
  add_header_above(c("TB prevalence"=1,"Precision"=6)) %>%
  add_header_above(c(" "=1,"Expected number microbiologically confirmed / number required in sample"=6))
Table 5: Sample size calculation for adults.
Expected number microbiologically confirmed / number required in sample
TB prevalence
Precision
5% 6% 7% 8% 10% 15%
1% 139 / 13900 97 / 9700 71 / 7100 55 / 5500 35 / 3500 16 / 1600
2% 139 / 6950 97 / 4850 71 / 3550 55 / 2750 35 / 1750 16 / 800
3% 139 / 4634 97 / 3234 71 / 2367 55 / 1834 35 / 1167 16 / 534
4% 139 / 3475 97 / 2425 71 / 1775 55 / 1375 35 / 875 16 / 400
5% 139 / 2780 97 / 1940 71 / 1420 55 / 1100 35 / 700 16 / 320
8% 139 / 1738 97 / 1213 71 / 888 55 / 688 35 / 438 16 / 200
9% 139 / 1545 97 / 1078 71 / 789 55 / 612 35 / 389 16 / 178
10% 139 / 1390 97 / 970 71 / 710 55 / 550 35 / 350 16 / 160
12% 139 / 1159 97 / 809 71 / 592 55 / 459 35 / 292 16 / 134
15% 139 / 927 97 / 647 71 / 474 55 / 367 35 / 234 16 / 107
20% 139 / 695 97 / 485 71 / 355 55 / 275 35 / 175 16 / 80
27% 139 / 515 97 / 360 71 / 263 55 / 204 35 / 130 16 / 60

The resulting number \(n_{unadj}\) needs to be adjusted for the TB prevalence, \(p_{TB}\):

\[ n_{adj}=\frac{n_{unadj}}{p_{TB}} \]

We further need to take into account different levels of expected attrition (due to loss-to-follow-up; unusable data or other causes):

\[ n_{final}=\frac{n_{adj}}{1-p_{attr}} \]

where \(p_{attr}\) is the expected proportion of attrition.

The studies will be analyzed individually by countries, and then as a single multi-country evaluation. The latter aims to achieve the requirements of data for WHO guidance, which requests at least 250 individuals with bacteriologically confirmed TB.

As shown in Table 6, the total sample size to be enrolled varies with the average TB prevalence, target precision and expected loss-to-follow-up (individuals not completing a diagnostic process) in the individual countries and sites. However, with the assumptions shown in Table 6, and assuming an overall proportion of 50% people living with HIV (PLHIV) adult participants, we estimate a sample size of 7,200 PLHIV and 7,200 HIV-negative persons will include at least 548 PLHIV and 548 HIV-negative persons (total 1,096) with a microbiologically confirmed diagnosis of TB.

It is expected that countries with a high burden of HIV-associated TB (Brazil, Cameroon, Kenya, Malawi, Nigeria) will recruit an even higher ratio of PLHIV to HIV-negative participants. If the recruitment of PLHIV is lower than expected, countries with high burden of HIV-associated TB will enrich recruitment towards this population by recruiting in HIV care and treatment clinics and outpatient centres.

The number of adult males is expected to be higher than the number of females, as more males are affected by TB and a higher proportion of males than females attend primary and secondary clinics. The proportion of participants with TB that is obtained at the end of Phase 1 may vary from the expected proportion of participants with TB due to the inclusion of ICF and ACF in the study in settings in which they are not normally applied (i.e. ICF being conducted in PHC clinics). Additionally, some populations maybe feel more encouraged or discouraged to participate in the study, which may be reflected in the proportion of TB cases for a particular setting and the reasons behind this may be elucidated during the Realist Evaluation.

Code
dfSSCountry<-data.frame(
  country=c(rep("Cameroon",4),rep("Nigeria",4),rep("Kenya",2),rep("Bangladesh",3),rep("Brazil",2),rep("Viet Nam",3),rep("Malawi",1),"TOTAL"),
  setting=c("PHC clinics","District hospital","Informal settlement / rural poor","Children","PHC clinics","District hospital","Nomads","Internally displaced people","PHC clinics","District hospital","PHC clinics","District hospital","Informal settlements / rural poor","PHC clinics Aracaju","PHC clinics Maceio","District hospital","Informal settlements","Children","PHC clinics",NA),
  expectedTbProp=c(paste(sep="",c(9,8,5,4,10,20,8,12,9,20,5,3,5,15,27,2,1,0,6),"%"),NA),
  targetPrecision=c(paste(sep="",c(7,7,10,15,6.5,5.5,7,6,8,5.5,9,12,9,7,5.5,15,17,NA,8),"%"),NA),
  expectedAttrition=c(paste(sep="",c(20,12,15,20,18,18,12,20,12,18,14,16,14,5,10,20,20,NA,20),"%"),NA),
  sampleSize=c(c(1000,1000,800,500,1000,700,1000,1000,700,700,1000,1000,1000,500,500,1000,1500,500,1200),NA),
  expectedAdultTB=c(72,70,34,NA,82,114,70,96,55,114,43,26,43,71,121,16,12,NA,57,1096),
  adults=c(2800,rep(NA,3),3700,rep(NA,3),1400,NA,3000,rep(NA,2),1000,NA,2500,rep(NA,2),1200,14400),
  children=c(500,rep(NA,3),rep(NA,4),rep(NA,2),rep(NA,3),rep(NA,2),500,rep(NA,2),NA,1000)
)

dfSSCountry[dfSSCountry=="NA%"]<-NA

dfSSCountry %>%
  dplyr::select(!country) %>%
  kable(row.names=FALSE,col.names=c("Country & setting","Expected proportion with TB","Target precision","Expected attrition","Sample size","Expected adults with TB","Adults","Children"),format.args = list(big.mark = ",")) %>%
  kable_styling(full_width = FALSE) %>%
  pack_rows("Cameroon",1,4) %>%
  pack_rows("Nigeria",5,8) %>%
  pack_rows("Kenya",9,10) %>%
  pack_rows("Bangladesh",11,13) %>%
  pack_rows("Brazil",14,15) %>%
  pack_rows("Viet Nam",16,18) %>%
  pack_rows("Malawi",19,19) %>%
  pack_rows("TOTAL",20,20)
Table 6: Sample size for each survey.
Country & setting Expected proportion with TB Target precision Expected attrition Sample size Expected adults with TB Adults Children
Cameroon
PHC clinics 9% 7% 20% 1,000 72 2,800 500
District hospital 8% 7% 12% 1,000 70 . .
Informal settlement / rural poor 5% 10% 15% 800 34 . .
Children 4% 15% 20% 500 . . .
Nigeria
PHC clinics 10% 6.5% 18% 1,000 82 3,700 .
District hospital 20% 5.5% 18% 700 114 . .
Nomads 8% 7% 12% 1,000 70 . .
Internally displaced people 12% 6% 20% 1,000 96 . .
Kenya
PHC clinics 9% 8% 12% 700 55 1,400 .
District hospital 20% 5.5% 18% 700 114 . .
Bangladesh
PHC clinics 5% 9% 14% 1,000 43 3,000 .
District hospital 3% 12% 16% 1,000 26 . .
Informal settlements / rural poor 5% 9% 14% 1,000 43 . .
Brazil
PHC clinics Aracaju 15% 7% 5% 500 71 1,000 .
PHC clinics Maceio 27% 5.5% 10% 500 121 . .
Viet Nam
District hospital 2% 15% 20% 1,000 16 2,500 500
Informal settlements 1% 17% 20% 1,500 12 . .
Children 0% . . 500 . . .
Malawi
PHC clinics 6% 8% 20% 1,200 57 1,200 .
TOTAL
. . . . . 1,096 14,400 1,000

3.4 Objectives

3.4.1 Primary objective

To evaluate the performance of selected TB screening tests and combinations of such tests.

3.4.2 Secondary objectives

  • To identify test combinations that increase the proportion of people diagnosed with microbiologically confirmed TB.

  • To demonstrate that combinations of current and newer TB tests can facilitate using these tests in locations where they are not currently available.

4 Data simulation

In order to demonstrate the planned analyses and show computer code, we will simulate data like the one expected to be generated by the study.

Code
set.seed(123)

sensSpecMatHiv<-data.frame(
  sens=rnorm(n=7,mean=0.7,sd=0.02),
  spec=rnorm(n=7,mean=0.6,sd=0.01)
)
rownames(sensSpecMatHiv)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatW4ss<-data.frame(
  sens=rnorm(n=7,mean=0.80,sd=0.02),
  spec=rnorm(n=7,mean=0.65,sd=0.01)
)
rownames(sensSpecMatW4ss)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatSsm<-data.frame(
  sens=rnorm(n=7,mean=0.70,sd=0.02),
  spec=rnorm(n=7,mean=0.95,sd=0.01)
)
rownames(sensSpecMatSsm)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatCrp<-data.frame(
  sens=rnorm(n=7,mean=0.9,sd=0.02),
  spec=rnorm(n=7,mean=0.95,sd=0.01)
)
rownames(sensSpecMatCrp)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatLam<-data.frame(
  sens=rnorm(n=7,mean=0.82,sd=0.02),
  spec=rnorm(n=7,mean=0.9,sd=0.01)
)
rownames(sensSpecMatLam)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatPoolxpert<-data.frame(
  sens=rnorm(n=7,mean=0.92,sd=0.01),
  spec=rnorm(n=7,mean=0.97,sd=0.01)
)
rownames(sensSpecMatPoolxpert)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatCxr<-data.frame(
  sens=rnorm(n=7,mean=0.88,sd=0.025),
  spec=rnorm(n=7,mean=0.88,sd=0.02)
)
rownames(sensSpecMatCxr)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")

sensSpecMatXpert<-data.frame(
  sens=rnorm(n=7,mean=0.98,sd=0.005),
  spec=rnorm(n=7,mean=0.99,sd=0.005)
)
sensSpecMatXpert$sens[sensSpecMatXpert$sens>=0.99999]<-0.99999
sensSpecMatXpert$spec[sensSpecMatXpert$spec>=0.99999]<-0.99999
rownames(sensSpecMatXpert)<-c("Cameroon","Nigeria","Kenya","Bangladesh","Brazil","Viet Nam","Malawi")


dfSim<-data.frame(
  PID=c(paste(sep="_","Ca",1:3300),paste(sep="_","Ni",1:3400),paste(sep="_","Ke",1:1400),paste(sep="_","Ba",1:3000),paste(sep="_","Br_Ar",1:500),paste(sep="_","Br_Ma",1:500),paste(sep="_","Vi",1:3000),paste(sep="_","Ma",1:1200)),
  region=c(rep("",3300),rep("ZRC",1400),rep("JHF",2000),rep("",1400+3000),rep("Aracaju",500),rep("Maceio",500),rep("",3000+1200)),
  country=c(rep("Cameroon",3300),rep("Nigeria",3400),rep("Kenya",1400),rep("Bangladesh",3000),rep("Brazil",1000),rep("Viet Nam",3000),rep("Malawi",1200)),
  region=c(rep("",3300+3400+1400+3000),rep("Aracaju",500),rep("Maceio",500),rep("",3000+1200)),
  setting=c(rep("PHC",1000),rep("District",1000),rep("Nomads",800),rep("Children",500),rep("PHC",700),rep("District",700),rep("Nomads",1000),rep("IDP / refugees",1000),rep("PHC",700),rep("District",700),rep("PHC",1000),rep("District",1000),rep("Informal settlements",1000),rep("PHC",1000),rep("District",1000),rep("Informal settlements",1500),rep("Children",500),rep("PHC",1200)),
  reference=c(rbinom(n=1000,size=1,prob=0.09),rbinom(n=1000,size=1,prob=0.08),rbinom(n=800,size=1,prob=0.05),rbinom(n=500,size=1,prob=0.04),rbinom(n=700,size=1,prob=0.20),rbinom(n=700,size=1,prob=0.20),rbinom(n=1000,size=1,prob=0.08),rbinom(n=1000,size=1,prob=0.12),rbinom(n=700,size=1,prob=0.09),rbinom(n=700,size=1,prob=0.20),rbinom(n=1000,size=1,prob=0.05),rbinom(n=1000,size=1,prob=0.03),rbinom(n=1000,size=1,prob=0.05),rbinom(n=500,size=1,prob=0.15),rbinom(n=500,size=1,prob=0.27),rbinom(n=1000,size=1,prob=0.02),rbinom(n=1500,size=1,prob=0.01),rbinom(n=500,size=1,prob=0.001),rbinom(n=1200,size=1,prob=0.08))
  ) %>%
  dplyr::mutate(
    age=rpois(n=n(),lambda=28),
    sex=sample(size=n(),c("Female","Male"),prob=c(0.5,0.5),replace=TRUE),
    hiv=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatHiv[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatHiv[country,"sens"])),
    w4ss=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatW4ss[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatW4ss[country,"sens"])),
    ssm=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatSsm[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatSsm[country,"sens"])),
    crp=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatCrp[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatCrp[country,"sens"])),
    lam=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatLam[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatLam[country,"sens"])),
    poolxpert=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatPoolxpert[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatPoolxpert[country,"sens"])),
    cxr=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatCxr[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatCxr[country,"sens"])),
    xpert=case_when(reference==0~rbinom(n=n(),size=1,prob=1-sensSpecMatXpert[country,"spec"]),reference==1~rbinom(n=n(),size=1,prob=sensSpecMatXpert[country,"sens"]))
) %>%
  dplyr::mutate(
    ageCentered=age-20,
  )

dfAge<-rcs(dfSim$ageCentered,parms=5)
colnames(dfAge)<-paste(sep="","ageSpline",1:4)
ageKnots<-attributes(dfAge)$parms

dfSim<-dfSim %>%
  dplyr::mutate(
    ageSpline1=as.vector(dfAge[,1]),
    ageSpline2=as.vector(dfAge[,2]),
    ageSpline3=as.vector(dfAge[,3]),
    ageSpline4=as.vector(dfAge[,4]),
  )

dfSim[sample(size=10,replace=F,x=1:nrow(dfSim)),] %>%
  kable(row.names=FALSE) %>%
  kable_styling(full_width=FALSE)
write.csv(dfSim,file=paste(sep="","simData_S4A_",gsub(Sys.Date(),pattern="-",replacement=""),".csv"),row.names=F)
Table 7: 10 random rows from the simulated data.
PID region country region.1 setting reference age sex hiv w4ss ssm crp lam poolxpert cxr xpert ageCentered ageSpline1 ageSpline2 ageSpline3 ageSpline4
Ca_1564 Cameroon District 0 33 Female 0 1 0 0 0 0 0 0 13 13 7.5236448 1.7162630 0.3910035
Ni_2682 JHF Nigeria IDP / refugees 0 21 Male 0 0 1 0 0 0 0 0 1 1 0.0034602 0.0000000 0.0000000
Vi_487 Viet Nam District 0 31 Male 0 0 0 0 0 0 0 0 11 11 4.6055363 0.7474048 0.0934256
Ni_2859 JHF Nigeria IDP / refugees 0 28 Female 1 0 0 0 0 0 0 0 8 8 1.7716263 0.0934256 0.0000000
Vi_2636 Viet Nam Children 0 24 Female 0 0 0 0 1 0 0 0 4 4 0.2214533 0.0000000 0.0000000
Ca_2744 Cameroon Nomads 0 31 Female 0 1 0 0 0 0 0 0 11 11 4.6055363 0.7474048 0.0934256
Ni_1782 JHF Nigeria Nomads 0 34 Female 1 0 0 0 0 0 0 0 14 14 9.2301038 2.3356401 0.6072664
Ba_1328 Bangladesh District 0 24 Female 0 0 0 0 0 0 0 0 4 4 0.2214533 0.0000000 0.0000000
Ba_2870 Bangladesh Informal settlements 0 37 Male 0 1 1 0 0 0 0 0 17 17 14.8823529 4.4844291 1.4013841
Ba_2049 Bangladesh Informal settlements 0 26 Female 0 1 0 0 0 0 0 0 6 6 0.7474048 0.0034602 0.0000000

Table 7 shows 10 random rows from the data frame containing all 16300 simulated observations.

5 Statistical Analysis Plan

The R code to generate the results is embedded in this document. By default it is hidden, but can be displayed by clicking on the Code boxes on the right hand side.

5.1 General considerations

The reporting of this study will be prepared in accordance with the STARD and (Bossuyt et al. 2015) and STROBE (von Elm et al. 2007) guidelines.

All continuous data variables will be summarized using the following descriptive statistics:

  • N (size of relevant analysis population)
  • n (size of analysis population without missing values)
  • proportion of complete data for each variable (n/N)
  • arithmetic mean (or geometric mean if more appropriate)
  • standard deviation (SD)
  • median
  • 25th percentile value (P25), 75th percentile value (P75) and interquartile range (IQR)
  • minimum and maximum (where relevant)

The proportion / percentage of observed levels will be reported for all binary and categorical measures. When appropriate, corresponding exact 95% confidence intervals (CIs) for proportions will be included.

For statistical test, a significance level of 5% will be used. All p-values will be reported to 3 decimal digits.

5.1.1 Reporting conventions

P-values \(\geq 0.001\) and \(\leq 0.999\) will be reported to 3 decimal places; p-values less than 0.001 will be reported as “< 0.001”. The mean, standard deviation, median, IQR and other statistics will be reported to one decimal place greater than the original data. Minimum and maximum values will use the same number of decimal places as the original data. Proportions will be presented as two decimal places; values greater than zero but \(<0.01\) will be presented as “< 0.01”. Percentages will be reported to 2 decimal places; values greater than zero but \(<0.01\%\) will be presented as “< 0.01%”; values greater than \(99.99\%\) but less than \(100\%\) will be reported as “> 99.99%”. Estimated parameters, not on the same scale as raw observations (e.g. regression coefficients) will be reported to 3 significant figures.

5.1.2 Missing data

We expect only marginal missing data - for many participants all required samples will be taken on their first and only study visit. However, there can be more than one study visit. A person is approached on first entering the healthcare facility. If they are unable to give any of the samples they are asked to return the next day. The health care workers will follow-up a maximum of 3 times over a 1 week period. Any missing data that do arise we expect to be mainly due to technical reasons unrelated to the outcome of interest (but could arise also from the above visit schedule if not all samples are taken on the first visit).

We do expect that there will be some missing data, both for individual tests as well as for the reference standard and participant demographic and clinical data. Since we are adopting a Bayesian framework, we can easily incorporate missing data imputation as part of the MCMC process: by specifying distributions for all parameters of interest, the MCMC algorithm will consider any missing data to be no different than the unknown parameters of interest to our investigation. It will essentially impute a value for each sample at each iteration in the MCMC chains and incorporate the uncertainty of that imputation through the distribution building up over the full set of MCMC chains and iterations.

5.1.3 Technical details

The R environment for statistical computing (v4.5.0 or later) will be used for all analyses.

The Bayesian models are implemented in JAGS, v4.3.2.

All analysis code will be made publicly available under an MIT or GNU GPL v3.0 license on GitHub.

5.2 Primary objective analyses

For the primary objective we will estimate performance metrics for individual screening tests as well as combinations (“screening algorithms”) of tests. Specifically, we will estimate and report for each test and each test combination:

  • Sensitivity (Se)

  • Specificity (Sp)

  • Positive predictive value (PPV)

  • Negative predictive value (NPV)

  • Likelihood ratio for a positive test result (LR+) = Se/(1-Sp)

  • Likelihood ratio for a negative test result (LR-) = (1-Se)/Sp

  • Diagnostic yield per test (DYT), the proportion of positive tests

Our primary focus, from a performance point of view, will be on sensitivity and specificity, but there other metrics are of interest too, hence why we report them as well.

As we will use a Bayesian approach (see Section 5.2.5), we will estimate posterior distributions for each of these parameters, for each evaluated algorithm in each setting. We will summarise these distributions using the posterior mean and the quantile-based Bayesian confidence interval given by the 2.5th and 97.5th percentiles of the posterior distribution.

In addition to providing tables with these posterior point estimates, we will also construct forest plots showing sensitivities and specificities for each algorithm.

5.2.1 Facility and community analyses

Start 4 All populations use a mix of passive case finding (PCF), intensified case finding (ICF) for facility-based case finding and active finding (ACF) for community-based case finding. Within facility-based screening, we will distinguish between lowest level of care and district / referral level of care.

The modelling approach detailed below will be applied to each of the populations and settings for Start 4 All. What will differ however are the specific algorithms being evaluated for facility-based case finding on the one hand and community based case finding on the other (see further below for the list of algorithms evaluated).

5.2.2 Thresholds for diagnostic tests: CRP and CXR-CAD

We will use the following thresholds to define positive/negative status for the following assays:

  • Quantitative CRP:

    • Positive if CRP \(\geq 5\, mg/L\), negative otherwise.
  • Semi-quantitative CRP:

    • Positive if CRP-RDT is one of “10-40”, “40-80”, “>80”, negative if CRP-RDT is “<10”.
  • CXR-CAD:

    • Facility-based: positive if CAD (Qure-ai) score \(\geq 0.5\), negative otherwise,
    • Community-based: positive if CAD (Qure.ai) score \(\geq 0.3\), negative otherwise.

These are the threshold we will use for the primary analysis. For secondary analyses, we will use:

  • Quantitative CRP with threshold \(\geq 10\, mg/L\),
  • CXR-CAD AI score with thresholds \(\geq 0.3\) (in facility-based) and \(\geq 0.5\) (in community-based).

Note that for the primary analysis we will only use quantitative CRP, but sensitivity analyses will use semi-quantitative CRP and a hybrid of quantitative and semi-quantitative CRP (positive if either is positive).

5.2.3 Stratification

All analyses specified in this SAP will be for adult participants only (defined as 15 or above, though due to local regulation some countries recruited 18 or above only). While Viet Nam and Cameroon also recruited children (14 or below), there will be a separate SAP for the paediatric performance evluation.

All analyses, primary as well as secondary, will be stratified by:

  • Country (but for facility-based we will also compute results pooled across countries).
  • Facility-based (PCF/ICF) vs. community / key population-based (ACF) recruitment.
  • Within facility-based recruitment: PLHIV vs people with negative or unknown HIV status (but we will also compute results for all participants regardless of HIV status).
  • Within facility-based recruitment: lowest level of care vs. district level.
  • Within key populations: recruitment via W4SS or CXR-CAD recruitment. We will also analysis a hybrid recruitment of either W4SS or CAD-CXR positive screening.

In summary, analyses (primary as well as secondary) will be done in the following groups:

  1. Community-based, W4SS positive screening (separately for each country and key population).
  2. Community-based, CAD-CXR positive screening (separately for each country and key population).
  3. Community-based, W4SS pr CAD-CXR positive screening (separately for each country and key population).
  4. Facility-based, lowest level of care, all participants (separately per country and aggregate across countries).
  5. Facility-based, lowest level of care, PLHIV (separately per country and aggregate across countries).
  6. Facility-based, lowest level of care, people with negative or unknown HIV status (separately per country and aggregate across countries).
  7. Facility-based, district level, all participants (separately per country and aggregate across countries).
  8. Facility-based, district level, PLHIV (separately per country and aggregate across countries).
  9. Facility-based, district level, people with negative or unknown HIV status (separately per country and aggregate across countries).

5.2.4 Tuberculosis screening and diagnostic algorithms

For notation, to denote that we will use --> to refer to one assay followed by another assay and | to refer to assays being administered in parallel. In other words, combination A | B --> C --> D refers to a an algorithm where if either assay A or assay B gives a positive result (or both are positive) then proceed to assay C, and if that one is positive, then proceed to assay D (and record the outcome from that assay), else record a negative result.

5.2.4.1 Facility-based

We will distinguish between lowest level of care and district-level settings, but the algorithms evaluated are the same.

  1. Xpert Ultra with pooling --> Xpert Ultra
  2. Xpert Ultra
  3. LAM
  4. CXR-CAD --> Xpert Ultra with pooling --> Xpert Ultra
  5. CRP --> Xpert Ultra with pooling --> Xpert Ultra
  6. LAM --> Xpert Ultra with pooling --> Xpert Ultra
  7. CXR-CAD | CRP | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  8. CXR-CAD | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  9. CRP | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  10. CXR-CAD | CRP --> Xpert Ultra with pooling --> Xpert Ultra
  11. CXR-CAD --> Xpert Ultra
  12. CRP --> Xpert Ultra
  13. LAM --> Xpert Ultra
  14. CXR-CAD | CRP | LAM --> Xpert Ultra
  15. CXR-CAD | LAM --> Xpert Ultra
  16. CRP | LAM --> Xpert Ultra
  17. CXR-CAD | CRP --> Xpert Ultra
  18. CXR-CAD --> LAM
  19. CRP --> LAM
  20. CXR-CAD | CRP --> LAM

5.2.4.2 2. Community-based

We will distinguish between W4SS positive, CAD-CXR positive and a hybrid approach (either of W4SS, CAD-CXR positive) entry into the study. Conditional on the first step, the algorithms evaluated differ slightly depending on whether or not the first step involved CXR-CAD.

5.2.4.2.1 Community-based - W4SS entry

To note that these are the exact same algorithms as for PCF/ICF (both have W4SS entry into the study).

  1. Xpert Ultra with pooling --> Xpert Ultra
  2. Xpert Ultra
  3. LAM
  4. CXR-CAD -> Xpert Ultra with pooling --> Xpert Ultra
  5. CRP -> Xpert Ultra with pooling --> Xpert Ultra
  6. LAM -> Xpert Ultra with pooling --> Xpert Ultra
  7. CXR-CAD | CRP | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  8. CXR-CAD | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  9. CRP | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  10. CXR-CAD | CRP --> Xpert Ultra with pooling --> Xpert Ultra
  11. CXR-CAD --> Xpert Ultra
  12. CRP --> Xpert Ultra
  13. LAM --> Xpert Ultra
  14. CXR-CAD | CRP | LAM --> Xpert Ultra
  15. CXR-CAD | LAM --> Xpert Ultra
  16. CRP | LAM --> Xpert Ultra
  17. CXR-CAD | CRP --> Xpert Ultra
  18. CXR-CAD --> LAM
  19. CRP --> LAM
  20. CXR-CAD | CRP --> LAM
5.2.4.2.2 Community-based - CXR-CAD entry or (CXR-CAD or W4SS) entry

The algorithms evaluated for the CXR-CAD entry to the study in community-based screening or the hybrid approach where either a positive W4SS screening or a positive CXR-CAD makes a participant eligible for the study are the same.

  1. Xpert Ultra with pooling --> Xpert Ultra
  2. Xpert Ultra
  3. LAM
  4. CRP --> Xpert Ultra with pooling --> Xpert Ultra
  5. LAM --> Xpert Ultra with pooling --> Xpert Ultra
  6. CRP | LAM --> Xpert Ultra with pooling --> Xpert Ultra
  7. CRP --> Xpert Ultra
  8. LAM --> Xpert Ultra
  9. CRP | LAM --> Xpert Ultra
  10. CRP --> LAM

5.2.5 Estimation

We will estimate all of the probability parameters using Bayesian statistics. In Bayesian estimation, rather than calculating point estimates for each parameter of interest, the parameters are considered to be random variables and one estimates a distribution for each parameter. In Bayesian estimation, we estimate the posterior probability density of a given parameter; posterior because it is the best-fit distribution after we have observed data. The posterior distribution summarises our beliefs about the likely values for the parameter of interest and it is an update of our beliefs about the parameter before we had observed data – the prior distribution. The data are generated by a stochastic process and we can compute the likelihood of the data under a distribution for this process. To summarise:

\[\mbox{posterior} = \mbox{prior} \times \mbox{likelihood}\]

For Phase 1 of the Start 4 All programme, the parameters we are mostly interested in sensitivities, specificities, PPVs, NPVs of individual tests and test combinations, conditional PPVs and NPVs and proportions of positive tests at each step of a test combination, TB prevalence) are all probability parameters. The exception will be the positive and negative likelihood ratios, LR+, LR-, but they can be directly derived from the sensitivities and specificieties. The natural parametric distribution for probability parameters is the beta distribution as it has support over the interval \([0,1]\).

A random variable \(X\) follows a beta distribution with parameters \(a,b\), \(X\sim\beta(a,b)\), if

\[ p(x)=\begin{cases} \frac{x^{a-1}(1-x)^{b-1}}{B(a,b)} &\mbox{ if }x\in[0,1] \\ 0 &\mbox{ otherwise} \end{cases} \]

where \(B(a,b)=\frac{\Gamma(a)\Gamma(b)}{\Gamma(a+b)}\).

The mean of this distribution is given by \(E(X)=\frac{a}{a+b}\) and the variance is \(Var(X)=\frac{a\cdot b}{(a+b)^2(a+b+1)}\). The mode is given by \(\frac{a-1}{a+b-2}\), but this is only defined if \(a,b>1\).

This means that for each probability parameter we plan to estimate within Start 4 All, we can assume a beta prior distribution, \(p\sim\beta(a,b)\), where \(p\) is the probability parameter of interest. The corresponding data likelihood will be a binomial likelihood \(X\sim Bin(n,p)\), where \(X\) is the number of positive results (possibly conditional on prior tests), \(n\) the corresponding total number of observations (aka the denominator) and \(p\) the same probability parameter as in the prior. One can show that the posterior will also be a beta distribution (since the beta is the conjugate prior for a binomial likelihood) with a closed form solution: \(p|X=k\sim\beta(k+a,n-k+b)\). The closed-form solution is computationally fast to compute - no need to use Markov Chain Monte Carlo (MCMC) or Integrated Nested Lapace Approximation (INLA).

For prior, a weakly informative prior can be used, for example the Kerman prior \(\beta(1/3,1/3)\) (Kerman 2011) or the improper Haldane prior \(\beta(0,0)\) (Haldane 1948). The latter has the advantage that it essentially does not provide any prior information and the posterior mean corresponds exactly to the sample mean. The drawback of the Haldane prior is, however, that where k=0 or k=n, it yields an improper posterior distribution. Another drawback is that JAGS, the MCMC sampler we are using, does not allow improper priors. For this reason, we will use the Kerman prior.

Specifically, with the Kerman prior, for a probability parameter of interest \(p\), if we observe \(X=k\) positive results out of \(n\) total results, then the posterior distribution will be a beta distribution with parameters \(k+a=k+1/3\) and \(n-k+b=n-k+1/3\):

\[p|n,X=k\sim\beta(k+a,n-k+b)\]

The distributional parameters \(\alpha=k+a, \beta=n-k+b\) for the posterior will then be directly fed into the health economic modelling to guarantee correct propagation of errors.

The data we will collect is expected to be incomplete - some individuals will have missing results for some of the tests and some may even lack a reference test result, meaning we would not know their TB status.

For these reasons, we will use a hybrid approach, using both the closed form result from above as well as Markov Chain Monte Carlo (MCMC) estimation and modelling directly the individual test results for each test for each participant from each country and setting in the study.

To note that, when we use MCMC, only a relatively small subset of probability parameters (TB prevalence, sensitivities and specificities of individual tests) will generate all of the data, and we can, in theory, derive any and all conditional probability parameters (e.g. P(positive LAM | positive CAD-CXR)) directly from this small subset. However this requires some assumptions, specifically that conditional on TB status, the different tests are independent. For Xpert and pooled Xpert, which use the same technology, this can easily be seen not to be true and so we will still derive all conditional parameters individually, but using the MCMC process to impute the missing data while incorporating the uncertainty of these imputations into our estimations.

All Bayesian models will be implemented in JAGS, we will run 4 chains, with each chain run for 3,000 adaptive iterations, 1,000 burn-in iterations and 12,000 iterations that are retained for statistical inference. Depending on MCMC diagnostics for mixing and non-convergence, we may run more chains for longer if needed. (Note that the mock-ups in this SAP, for convenience, used only 4 chains, 1,000 adaptive iterations, 500 burn-in iterations and 2,000 iterations for inference.)

5.2.5.1 Per-country analysis

We will assume that the individual probability parameters are random variables. We will further assume that the set of sensitivity and specificity parameters for the different tests follow a non-trivial joint distribution and we will specify this in the MCMC model.

Let:

  • \(N\) be the number of participants in the study,
  • \(m=5\) be the number of tests / assays being evaluated,
  • \(T_i, i=1,\ldots,N\) be a binary variable recording the TB status of the \(i^{th}\) study participant (for the purpose of Start 4 All we take this as the result of the microbiological confirmation / reference test),
  • \(X_{j,i}, j=\mbox{cxr, crp, lam, pXpert, sXpert}, i=1,\ldots,N\) be the individual test results for the different participants,
  • \(p_{se,j}\), \(p_{sp,j}\) be the sensitivity and specificity of test \(j\),
  • \(p_{TB}\) be the prevalence of TB in the population.

With this notation, we can write down the assumptions of the Bayesian MCMC estimation:

  1. TB status is Bernoulli distributed.

\[T_i\sim\mbox{Bernoulli}(p_{TB})\] with \(\mbox{logit}(p_{TB})=\beta_0+\beta_{s}\cdot\mbox{sex}+\beta_{h}\cdot\mbox{hiv}+\mathbb{\beta}_a\cdot s(\mbox{age})\), assuming weakly informative \(\cal{N}(0,10)\) priors for the \(\beta_j\) parameters. \(s()\) indicates a restricted cubic / natural spline with 5 knots and \(\mathbb{\beta}_a\) is the corresponding vector of parameters. For the analyses stratified by HIV status, the HIV term is dropped from the linear predictor term.

  1. Individual test results are Bernoulli distributed, conditional on TB status.

\[X_{j,i}\sim\left\{ \begin{array}{ll} \mbox{Bernoulli}(p_{se,j}) & \quad\mbox{ if }T_i=1 \\ \mbox{Bernoulli}(1-p_{sp,j}) & \quad\mbox{ if }T_i=0 \end{array} \right. \]

  1. Sensitivities, specificities are specified as the inverse logits of real scalars.

\[ \begin{array}{lll} p_{se,j} &=& \mbox{logit}^{-1}(z_{se,j}) \\ p_{sp,j} &=& \mbox{logit}^{-1}(z_{sp,j}) \end{array} \]

  1. These, or in other words, the logits of sensitivities, specificities, follow a non-trivial joint multivariate normal distribution.

\[\mathbb{z}=(z_{se,w4ss},z_{sp,w4ss},z_{se,cxr},z_{sp,cxr},\ldots,z_{se,sXpert},z_{sp,sXpert})\sim MV\cal{N}(\mathbb{\mu},\mathbb{\Sigma})\]

  1. Priors for the means of the logits are weakly informative.

\[ \begin{array}{lll} \mu_{se,j}\sim\cal{N}(0,10) \\ \mu_{sp,j}\sim\cal{N}(0,10) \end{array} \]

  1. Prior for the covariance matrix follow a satndard prior distribution for covariance matrices.

\[\Sigma\sim\mbox{invWishart}(\Lambda,\eta)\qquad \Lambda=I,\; \eta=2\cdot m+1\]

  1. Assuming that all dependencies between tests are captured through the above joint distribution of sensitivities and specificities and through conditioning on TB status, then all other, conditional, probability parameters can be derived from the sensitivity and specificity parameters. However, as explained earlier, the assumption of conditional independence is most likely unreasonable. For this reason, for each conditional parameter \(\pi|\mathbf{T}\) (where \(\mathbf{T}\) denote all test results upon which the probability parameter is conditional on), we will assume:

    • \(\pi|\mathbf{T}\sim\beta(a=K+1/3,b=M-K+1/3)\)

    • \(M,K\) are the random variables for the count of records for which the parameter is being evaluated (M) and the count of positive results (K) respectively and they are derived from the MCMC process (specifically \(T_i, X_{i,j}\)).

  2. This final distribution for \(\pi|\mathbf{T}\) is not necessarily a beta distribution itself (since M and K are random themselves). For the performance evaluation, we will report the distribution as is. However to feed this posterior distribution into the health economic modelling, we will assume that it can be approximated by one. We will use quantile-matching to identify the best-fitting beta distribution for the resulting empirical distribution from the MCMC output. The beta distribution parameters from this best-fit beta distribution are then entered into the health economic modelling.

5.2.5.2 Aggregate facility-based analysis at lowest level of care

For facility-based screening, we will combine the data from all Start 4 All partner countries to estimate a single set of performance metrics at i) lowest level of care and ii) district-level care.

There is a need to account for country-level variation in the estimation procedure described above.

Let:

  • \(N\) be the number of participants in the study,
  • \(m=5\) be the number of tests / assays being evaluated,
  • \(T_{l,i}\) be a binary variable recording the TB status of the \(i^{th}\) study participant from country \(l\),
  • \(X_{l,j,i}\) be the individual test result for participant \(i\) from country \(l\) for assay \(j\),
  • \(p_{se,l,j}\), \(p_{sp,l,j}\) be the sensitivity and specificity of test \(j\) in country \(l\),
  • \(p_{TB,l}\) be the prevalence of TB in the population in country \(l\).

where the indices \({i,j,l}\) iterate over:

  • \(l=\mbox{Bangladesh, Brazil, Cameroon, Kenya, Malawi, Nigeria, Viet Nam}\),
  • \(j=\mbox{cxr, crp, lam, pXpert, sXpert}\),
  • \(i=1,\ldots,N\)

With this notation, we can write down the assumptions for the Bayesian MCMC estimation:

  1. TB status is Bernoulli distributed.

\[T_{l,i}\sim\mbox{Bernoulli}(p_{TB,l})\] with \(\mbox{logit}(p_{TB})=\beta_0+\beta_{s}\cdot\mbox{sex}+\beta_{h}\cdot\mbox{hiv}+\mathbb{\beta}_a\cdot s(\mbox{age})\), assuming weakly informative \(\cal{N}(0,10)\) priors for the \(\beta_j\) parameters. \(s()\) indicates a restricted cubic / natural spline with 5 knots and \(\mathbb{\beta}_a\) is the corresponding vector of parameters. For the analyses stratified by HIV status, the HIV term is dropped from the linear predictor term.

  1. Individual test results are Bernoulli distributed, conditional on TB status.

\[ X_{l,j,i}\sim\left\{ \begin{array}{ll} \mbox{Bernoulli}(p_{se,l,j}) & \quad\mbox{ if }T_{l,i}=1 \\ \mbox{Bernoulli}(1-p_{sp,l,j}) & \quad\mbox{ if }T_{l,i}=0 \end{array} \right. \]

  1. Sensitivities, specificities are specified as the inverse logits of real scalars.

\[ \begin{array}{lll} p_{se,l,j} &=& \mbox{logit}^{-1}(z_{se,j}+\tau_{l,j}) \\ p_{sp,l,j} &=& \mbox{logit}^{-1}(z_{sp,j}+\nu_{l,j}) \end{array} \]

where \(\tau_{l,j}, \nu_{l,j}\) are country-level random effects.

  1. These, or in other words, the logits of sensitivities, specificities, follow a non-trivial joint multivariate normal distribution.

\[ \mathbb{z}=(z_{se,w4ss},z_{sp,w4ss},z_{se,cxr},z_{sp,cxr},\ldots,z_{se,sXpert},z_{sp,sXpert})\sim MV\cal{N}(\mathbb{\mu},\mathbb{\Sigma}) \]

  1. Priors for the means of the logits are weakly informative.

\[ \begin{array}{lll} \mu_{se,j}\sim\cal{N}(0,10) \\ \mu_{sp,j}\sim\cal{N}(0,10) \end{array} \]

  1. Prior for the covariance matrix follow a standard prior distribution for covariance matrices.

\[ \Sigma\sim\mbox{invWishart}(\Lambda,\eta)\qquad \Lambda=I,\; \eta=2\cdot m+1 \]

  1. Assuming that all dependencies between tests are captured through the above joint distribution of sensitivities and specificities and through conditioning on TB status, then all other, conditional, probability parameters can be derived from the sensitivity and specificity parameters. However, as explained earlier, the assumption of conditional independence is most likely unreasonable. For this reason, for each conditional parameter \(\pi|\mathbf{T}\) (where \(\mathbf{T}\) denote all test results upon which the probability parameter is conditional on), we will assume:

    • \(\pi|\mathbf{T}\sim\beta(a=K+1/3,b=M-K+1/3)\)

    • \(M,K\) are the random variables for the count of records for which the parameter is being evaluated (M) and the count of positive results (K) respectively and they are derived from the MCMC process (specifically \(T_{l,i}, X_{l,j,i}\)).

  2. This final distribution for \(\pi|\mathbf{T}\) is not necessarily a beta distribution itself (since M and K are random themselves). For the performance evaluation, we will report the distribution as is. However to feed this posterior distribution into the health economic modelling, we will assume that it can be approximated by one. We will use quantile-matching to identify the best-fitting beta distribution for the resulting empirical distribution from the MCMC output. The beta distribution parameters from this best-fit beta distribution are then entered into the health economic modelling.

5.2.5.3 Model diagnostics

For every Bayesian model fitted, we will check for signs of non-convergence of the MCMC algorithm. Specifically we will:

  • Inspect trace plots for all parameters (and, if needed, increase the number of chains and/or iterations per chain).

  • Compute Gelman-Rubin potential scale reduction factors for all parameters (and, if needed, increase the number of iterations).

  • Compute effect sample sizes for every parameter (and, if needed, increase the number of iterations).

  • Compute posterior predictive checks to assess the goodness of fit of the model.

5.3 Secondary analyses

These will include:

  • Repeating the main analysis but with alternative threshold for quantitative CRP and CXR-CAD AI score (see Section 5.2.2).

  • Using a composite reference standard of culture and molecular testing.

  • Modelling performance as a function of TB prevalence.

5.4 Sensitivity analyses

We will explore (see Section 5.2.2) using semi-quantitative CRP instead of quantitative CRP as well as a hybrid of quantitative and semi-quantitative CRP.

5.5 Mock results

This version of the SAP includes some mock-ups but not yet all mock-ups as they needed to be re-done completely given the change in algorithms being evaluated. While the analysis plan itself is complete, full mock-ups will only be included in a future version.

Code
parsVectW4ss<-as.vector(unlist(read.csv(header=F,"../../output/20250527/parameters_forRscript_w4ss.txt")[1,]))
parsVectCxr<-as.vector(unlist(read.csv(header=F,"../../output/20250527/parameters_forRscript_cxr.txt")[1,]))

parsListW4ss.ab<-character(0)
for(par in parsVectW4ss){
  if(par=="ptb"){
    parsListW4ss.ab<-c(parsListW4ss.ab,"ptb.a","ptb.b")
  }else{
    tmp<-unlist(strsplit(split="\\.",par))
    parsListW4ss.ab<-c(parsListW4ss.ab,paste(sep=".",tmp[1],"a",paste(collapse=".",tmp[-1])),paste(sep=".",tmp[1],"b",paste(collapse=".",tmp[-1])))
  }
}

parsListCxr.ab<-character(0)
for(par in parsVectCxr){
  if(par=="ptb"){
    parsListCxr.ab<-c(parsListCxr.ab,"ptb.a","ptb.b")
  }else{
    tmp<-unlist(strsplit(split="\\.",par))
    parsListCxr.ab<-c(parsListCxr.ab,paste(sep=".",tmp[1],"a",paste(collapse=".",tmp[-1])),paste(sep=".",tmp[1],"b",paste(collapse=".",tmp[-1])))
  }
}

jagsFileW4ss<-"../../scripts/S4A_jagsPerformanceModel_entryW4SS_20250527_clean.jags"
jagsFileW4ssNoHiv<-"../../scripts/S4A_jagsPerformanceModel_entryW4SS_20250527_clean.noHIVstatus.jags"
jagsFileCxr<-"../../scripts/S4A_jagsPerformanceModel_entryCXR_20250527_clean.jags"
jagsFileCxr.NoHiv<-"../../scripts/S4A_jagsPerformanceModel_entryCXR_20250527_clean.noHIVstatus.jags"
jagsFileW4ssPooled<-"../../scripts/S4A_jagsPerformanceModel_entryW4SS_pooledFacility_20250527_clean.jags"
jagsFileW4ssPooled.NoHiv<-"../../scripts/S4A_jagsPerformanceModel_entryW4SS_pooledFacility_20250527_clean.noHIVstatus.jags"

nChains<-4
nAdapt<-1000 # 3000 in full analysis
nBurn<-500 # 1000 in full analysis
nIter<-2000 # 12000 in full analysis

# define the parameters to extract from the MCMC run
parsVectW4ssNoLR<-parsVectW4ss
parsVectW4ss<-c(parsVectW4ss,gsub(pattern="pse",replacement="lrp",grep(value=TRUE,pattern="pse",parsVectW4ss)),gsub(pattern="pse",replacement="lrm",grep(value=TRUE,pattern="pse",parsVectW4ss)))

parsVectCxrNoLR<-parsVectCxr
parsVectCxr<-c(parsVectCxr,gsub(pattern="pse",replacement="lrp",grep(value=TRUE,pattern="pse",parsVectCxr)),gsub(pattern="pse",replacement="lrm",grep(value=TRUE,pattern="pse",parsVectCxr)))

# helper functions to process MCMC output
ssBetaPars<-function(abPars, probs, parData, alpha = 0.05) {
  res<-sum((qbeta(probs,abPars[1],abPars[2])-quantile(parData,probs=probs))^2)
  return(res)
}

identifyBetaPars<-function(probs=c(0.025,0.25,0.5,0.75,0.975), parData, maxiter = 1000){
  if (length(probs)<2 | sum(probs<0 | probs>1)>0) {
    stop("There need to be at least 2 probs parameters and the probs parameters need all to be contained within [0,1].")
  }
  
  m<-mean(parData)
  s<-var(parData)
  initPars<-c(m*(m*(1-m)/s-1),(1-m)*(m*(1-m)/s-1))
  
  res <- suppressWarnings(optim(fn = ssBetaPars, probs=probs, parData=parData, par = initPars, control = list(maxit = maxiter), alpha = alpha))
  if (res$convergence != 0) {
    stop("optim() as called by identifyBetaPars() failed to converge.")
  }
  return(res$par)
}

getBetaDistsFromMCMC<-function(mcmcPars,doPlot=FALSE,plotFile="s4a_mcmcPars.pdf"){
  parsAll<-colnames(mcmcPars[[1]])
  pars<-parsAll[!grepl(parsAll,pattern="lrm|lrp") & !(parsAll %in% c(paste(sep="","b",0:2),paste(sep="","bs",1:4)))]
  nChains<-length(mcmcPars)
  
  g<-list()
  
  resDf<-data.frame(
    par=pars,
    a=NA,
    b=NA,
    distMean=NA,
    dataMean=NA
  )
  
  parsDf<-mcmcPars[[1]]
  if(nChains>1){
    for(j in 2:nChains){
      parsDf<-rbind(parsDf,mcmcPars[[j]])
    }
  }
  
  for(par in pars){
    tmpPars<-identifyBetaPars(parData=parsDf[,par])
    resDf$a[resDf$par==par]<-tmpPars[1]
    resDf$b[resDf$par==par]<-tmpPars[2]
    resDf$dataMean[resDf$par==par]<-mean(parsDf[,par])
  }
  
  if(doPlot){
    pdf(width=4*2,height=4*ceiling(length(pars)/2),file=plotFile)
    par(mfrow=c(ceiling(length(pars)/2),2))
    for(par in parsAll){
      hist(freq=FALSE,parsDf[,par],breaks=100,xlab=par,ylab="density")
      if(par %in% pars){
        xx<-seq(min(parsDf[,par]),max(parsDf[,par]),length=1e4)
        yy<-dbeta(xx,resDf$a[resDf$par==par],resDf$b[resDf$par==par])
        lines(xx,yy,lwd=2,col="steelblue")
      }
    }
    dev.off()
  }
  
  resDf$distMean<-resDf$a/(resDf$a+resDf$b)
  
  return(resDf)
}

# analysis function
analysisFunBayesBetaPars<-function(dat,c,r,s,doPlot=FALSE,plotFile="s4a_mcmcPars.pdf",nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile,parsVect){
  # dat = data frame with the data; requires columns country, region, setting, reference, hiv, w4ss, crp, lam, poolxpert, cxr, xpert
  # c = country
  # r = region
  # s = setting
  # doPlot = (logical) idnicatees whether or not to plot the MCMC trace plots and posterior distributions; defaults to FALSE
  # plotFile = filename for the graph (if doPlot==TRUE)
  # nChains, nAdapt, nBurn, nIter = MCMC parameters (number of chains, adaptation iterations, burn-in iterations, MCMC iterations)
  # jagsFile = filename with path to the JAGS model file
  # parsVect = vector with parameters to extract from / monitor during the MCMC sampling
  
  # prepare the data
  dfTmp<-dat %>%
    dplyr::filter(country==c & region==r & setting==s)
  
  datJags<-list(
    N=nrow(dfTmp),
    hiv=dfTmp$hiv,
    sexM=ifelse(dfTmp$sex=="Male",1,0),
    ageSpline1=dfTmp$ageSpline1,
    ageSpline2=dfTmp$ageSpline2,
    ageSpline3=dfTmp$ageSpline3,
    ageSpline4=dfTmp$ageSpline4,
    reference=dfTmp$reference,
    crp=dfTmp$crp,
    cxr=dfTmp$cxr,
    lam=dfTmp$lam,
    poolxpert=dfTmp$poolxpert,
    xpert=dfTmp$xpert,
    ssm=dfTmp$ssm
  )
  
  # run the JAGS model
  jagsModel <- jags.model(jagsFile, data=datJags, n.chains = nChains, n.adapt = nAdapt, quiet = TRUE)
  update(jagsModel,nBurn)
  parsModel<-coda.samples(model=jagsModel,variable.names=parsVect,n.iter = nIter, na.rm=FALSE)
  
  # process the MCMC results
  betaPars<-getBetaDistsFromMCMC(mcmcPars=parsModel,doPlot=doPlot,plotFile=plotFile)
  
  # return the results
  return(list(mcmcObj=parsModel,betaPars=betaPars))
}

analysisFunBayesBetaParsPool<-function(dat,set,doPlot=FALSE,plotFile="s4a_mcmcPars_pool.pdf",nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile,parsVect){
  # dat = data frame with the data; requires columns country, region, setting, reference, hiv, w4ss, crp, lam, poolxpert, cxr, xpert
  # set = PHC or District
  # doPlot = (logical) idnicatees whether or not to plot the MCMC trace plots and posterior distributions; defaults to FALSE
  # plotFile = filename for the graph (if doPlot==TRUE)
  # nChains, nAdapt, nBurn, nIter = MCMC parameters (number of chains, adaptation iterations, burn-in iterations, MCMC iterations)
  # jagsFile = filename with path to the JAGS model file
  # parsVect = vector with parameters to extract from / monitor during the MCMC sampling
  
  # prepare the data
  dfTmp<-dat %>%
    dplyr::filter(setting==set) %>%
    dplyr::mutate(
      site=as.integer(factor(
       paste(sep="",country,region) 
      ))
    )
  
  datJags<-list(
    N=nrow(dfTmp),
    nsites=length(unique(dfTmp$site)),
    site=dfTmp$site,
    hiv=dfTmp$hiv,
    ageSpline1=dfTmp$ageSpline1,
    ageSpline2=dfTmp$ageSpline2,
    ageSpline3=dfTmp$ageSpline3,
    ageSpline4=dfTmp$ageSpline4,
    sexM=ifelse(dfTmp$sex=="Male",1,0),
    reference=dfTmp$reference,
    crp=dfTmp$crp,
    cxr=dfTmp$cxr,
    lam=dfTmp$lam,
    poolxpert=dfTmp$poolxpert,
    xpert=dfTmp$xpert,
    ssm=dfTmp$ssm
  )
  
  # run the JAGS model
  jagsModel <- jags.model(jagsFile, data=datJags, n.chains = nChains, n.adapt = nAdapt, quiet = TRUE)
  update(jagsModel,nBurn)
  parsModel<-coda.samples(model=jagsModel,variable.names=parsVect,n.iter = nIter, na.rm=FALSE)
  
  # process the MCMC results
  betaPars<-getBetaDistsFromMCMC(mcmcPars=parsModel,doPlot=doPlot,plotFile=plotFile)
  
  # return the results
  return(list(mcmcObj=parsModel,betaPars=betaPars))
}

getLRpLRm<-function(mcmcObj){
  nChains<-length(mcmcObj)
  
  for(j in 1:nChains){
    pars<-colnames(mcmcObj[[j]])
    pars<-gsub(pattern="pse",replacement="",pars[grepl(pattern="pse.",pars)])
    
    x.thin<-thin(mcmcObj[[j]])
    x.start<-start(mcmcObj[[j]])
    x.end<-end(mcmcObj[[j]])
    
    for(par in pars){
      pse<-mcmcObj[[j]][,paste(sep="","pse",par)]
      psp<-mcmcObj[[j]][,paste(sep="","psp",par)]
      
      mcmcObj[[j]]<-as.data.frame(mcmcObj[[j]]) %>%
        dplyr::mutate(
          !!paste(sep="","lrp",par) := pse/(1-ifelse(psp<0.99999,psp,0.99999)),
          !!paste(sep="","lrm",par) := (1-pse)/psp
        )
    }
    
    mcmcObj[[j]]<-mcmc(data=as.matrix(mcmcObj[[j]]),start=x.start,end=x.end,thin=x.thin)
  }
  
  return(mcmcObj)
}

gr<-expand.grid(c("pse","psp","ppv","npv","dyt","posProp","lrm","lrp"),c("Crp","Cxr","Lam","Poolxpert","Xpert","Ssm"))
parsVectPrim<-apply(MARGIN=1,FUN=paste,gr,collapse=".")

5.5.1 Descriptive statistics and summary statistics

We will summarise the collected data in a simple table as shown on Table 8.

Code
pTB<-mean(dfSim$reference,na.rm=T)
pTBCI<-binom.test(n=sum(!is.na(dfSim$reference)),x=sum(dfSim$reference,na.rm=TRUE))$conf.int
n<-nrow(dfSim)
nMissRef<-sum(is.na(dfSim$reference))
nMissAnyDiag<-sum(rowSums(is.na(dfSim[,c("reference","w4ss","crp","cxr","lam","xpert","poolxpert","ssm")]))>0)

dfSum<-data.frame(
  country="Any",
  setting="Any",
  n=n,
  nWithReference=sum(!is.na(dfSim$reference)),
  nTB=sum(dfSim$reference,na.rm=T),
  pTB=paste(sep="",format(nsmall=2,round(digits=2,100*pTB)),"% (",format(nsmall=2,round(digits=2,100*pTBCI[1])),"%,",format(nsmall=2,round(digits=2,100*pTBCI[2])),"%)"),
  missingRef=paste(sep="",nMissRef," (",format(nsmall=2,round(digits=2,100*nMissRef/n)),"%)"),
  missingAnyDiag=paste(sep="",nMissAnyDiag," (",format(nsmall=2,round(digits=2,100*nMissAnyDiag/n)),"%)")
)

dfSumStratified<-dfSim %>%
  dplyr::filter(country!="" & !is.na(setting)) %>%
  dplyr::group_by(country,setting) %>%
  dplyr::summarise(
    n=n(),
    nWithReference=sum(!is.na(reference)),
    nTB=sum(reference,na.rm=TRUE),
    pTB=ifelse(sum(!is.na(reference)>0),paste(sep="",format(nsmall=2,round(digits=2,100*mean(reference,na.rm=TRUE))),"%"," (",format(nsmall=2,round(digits=2,100*binom.test(x=sum(reference,na.rm=TRUE),n=sum(!is.na(reference)))$conf.int[1])),"%,",format(nsmall=2,round(digits=2,100*binom.test(x=sum(reference,na.rm=TRUE),n=sum(!is.na(reference)))$conf.int[2])),"%)"),NA),
    missingRef=paste(sep="",sum(is.na(reference))," (",format(nsmall=2,round(digits=2,100*sum(is.na(reference))/n())),"%)"),
    missingAnyDiag=paste(sep="",sum(is.na(reference+w4ss+crp+cxr+lam+xpert+poolxpert+ssm))," (",format(nsmall=2,round(digits=2,100*sum(is.na(reference+w4ss+crp+cxr+lam+xpert+poolxpert+ssm))/n())),"%)"),
    .groups="drop"
  ) 

dfSum<-rbind(dfSum,dfSumStratified)

dfSum %>%
  kable(col.names=c("Country","Setting","n","n (with available reference)","TB cases (culture)","TB prevalence (95% CI)","Culture not available (%)","Observations with at least one diagnostic missing (%)")) %>%
  kable_styling(full_width=FALSE)
Table 8: Summary of the study data.
Country Setting n n (with available reference) TB cases (culture) TB prevalence (95% CI) Culture not available (%) Observations with at least one diagnostic missing (%)
Any Any 16300 16300 1311 8.04% (7.63%,8.47%) 0 (0.00%) 0 (0.00%)
Bangladesh District 1000 1000 25 2.50% (1.62%,3.67%) 0 (0.00%) 0 (0.00%)
Bangladesh Informal settlements 1000 1000 49 4.90% (3.65%,6.43%) 0 (0.00%) 0 (0.00%)
Bangladesh PHC 1000 1000 34 3.40% (2.37%,4.72%) 0 (0.00%) 0 (0.00%)
Brazil PHC 1000 1000 215 21.50% (18.99%,24.18%) 0 (0.00%) 0 (0.00%)
Cameroon Children 500 500 17 3.40% (1.99%,5.39%) 0 (0.00%) 0 (0.00%)
Cameroon District 1000 1000 85 8.50% (6.85%,10.40%) 0 (0.00%) 0 (0.00%)
Cameroon Nomads 800 800 30 3.75% (2.54%,5.31%) 0 (0.00%) 0 (0.00%)
Cameroon PHC 1000 1000 79 7.90% (6.30%,9.75%) 0 (0.00%) 0 (0.00%)
Kenya District 700 700 138 19.71% (16.83%,22.86%) 0 (0.00%) 0 (0.00%)
Kenya PHC 700 700 59 8.43% (6.48%,10.74%) 0 (0.00%) 0 (0.00%)
Malawi PHC 1200 1200 93 7.75% (6.30%,9.41%) 0 (0.00%) 0 (0.00%)
Nigeria District 700 700 119 17.00% (14.29%,19.99%) 0 (0.00%) 0 (0.00%)
Nigeria IDP / refugees 1000 1000 117 11.70% (9.77%,13.86%) 0 (0.00%) 0 (0.00%)
Nigeria Nomads 1000 1000 77 7.70% (6.12%,9.53%) 0 (0.00%) 0 (0.00%)
Nigeria PHC 700 700 153 21.86% (18.85%,25.11%) 0 (0.00%) 0 (0.00%)
Viet Nam Children 500 500 0 0.00% (0.00%,0.74%) 0 (0.00%) 0 (0.00%)
Viet Nam District 1000 1000 10 1.00% (0.48%,1.83%) 0 (0.00%) 0 (0.00%)
Viet Nam Informal settlements 1500 1500 11 0.73% (0.37%,1.31%) 0 (0.00%) 0 (0.00%)

We will also tabulate the performance of individual tests per country and setting for two different reference standards (culture or Xpert Ultra).

Code
gr<-expand.grid(c("culture","xpert"),c("All","PLWithHIV","PLWithoutHIV"),unique(paste(sep="_",dfSim$country,dfSim$region,dfSim$setting)))
gr[,1]<-as.character(gr[,1])
gr[,2]<-as.character(gr[,2])
gr[,3]<-as.character(gr[,3])

dfPerf<-data.frame(
  Country=NA,
  Region=NA,
  Setting=NA,
  CRS=gr[,3],
  Reference=gr[,1],
  Population=gr[,2],
  Prevalence=NA,
  cultNegXpertNeg=NA,
  cultPosXpertNeg=NA,
  cultNegXpertPos=NA,
  cultPosXpertPos=NA,
  cxr_sens=NA,
  cxr_spec=NA,
  crp_sens=NA,
  crp_spec=NA,
  lam_sens=NA,
  lam_spec=NA,
  ssm_sens=NA,
  ssm_spec=NA,
  poolxpert_sens=NA,
  poolxpert_spec=NA,
  xpert_sens=NA,
  xpert_spec=NA
)

for(j in 1:nrow(dfPerf)){
  dfPerf$Country[j]<-unlist(strsplit(split="_",dfPerf$CRS[j]))[1]
  dfPerf$Region[j]<-unlist(strsplit(split="_",dfPerf$CRS[j]))[2]
  dfPerf$Setting[j]<-unlist(strsplit(split="_",dfPerf$CRS[j]))[3]
  
  dfTmp<-dfSim %>%
    dplyr::filter(country==dfPerf$Country[j] & region==dfPerf$Region[j] & setting==dfPerf$Setting[j])
  
  if(dfPerf$Population[j]=="PLWithHIV"){
    dfTmp<-dfTmp %>%
      dplyr::filter(!is.na(hiv) & hiv==1)
  }else if(dfPerf$Population[j]=="PLWithoutHIV"){
    dfTmp<-dfTmp %>%
      dplyr::filter(!is.na(hiv) & hiv==0)
  }
  
  if(dfPerf$Reference[j]=="culture"){refTmp<-dfTmp$reference}else{refTmp<-dfTmp$xpert}
  
  k<-sum(na.rm=TRUE,refTmp)
  n<-sum(!is.na(refTmp))
  
  dfPerf$Prevalence[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*k/n)),"% (",k,"/",n,")")
  
  dfPerf$cultNegXpertNeg[j]<-sum(!is.na(dfTmp$reference) & !is.na(dfTmp$xpert) & dfTmp$reference==0 & dfTmp$xpert==0)
  dfPerf$cultPosXpertNeg[j]<-sum(!is.na(dfTmp$reference) & !is.na(dfTmp$xpert) & dfTmp$reference==1 & dfTmp$xpert==0)
  dfPerf$cultNegXpertPos[j]<-sum(!is.na(dfTmp$reference) & !is.na(dfTmp$xpert) & dfTmp$reference==0 & dfTmp$xpert==1)
  dfPerf$cultPosXpertPos[j]<-sum(!is.na(dfTmp$reference) & !is.na(dfTmp$xpert) & dfTmp$reference==1 & dfTmp$xpert==1)
  
  #CXR
  tp<-sum(na.rm=TRUE,refTmp*dfTmp$cxr)
  fp<-sum(na.rm=TRUE,(1-refTmp)*dfTmp$cxr)
  tn<-sum(na.rm=TRUE,(1-refTmp)*(1-dfTmp$cxr))
  fn<-sum(na.rm=TRUE,refTmp*(1-dfTmp$cxr))
  dfPerf$cxr_sens[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tp/(tp+fn))),"% (",tp,"/",tp+fn,")")
  dfPerf$cxr_spec[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tn/(tn+fp))),"% (",tn,"/",tn+fp,")")
  
  #CRP
  tp<-sum(na.rm=TRUE,refTmp*dfTmp$crp)
  fp<-sum(na.rm=TRUE,(1-refTmp)*dfTmp$crp)
  tn<-sum(na.rm=TRUE,(1-refTmp)*(1-dfTmp$crp))
  fn<-sum(na.rm=TRUE,refTmp*(1-dfTmp$crp))
  dfPerf$crp_sens[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tp/(tp+fn))),"% (",tp,"/",tp+fn,")")
  dfPerf$crp_spec[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tn/(tn+fp))),"% (",tn,"/",tn+fp,")")
  
  #LAM
  tp<-sum(na.rm=TRUE,refTmp*dfTmp$lam)
  fp<-sum(na.rm=TRUE,(1-refTmp)*dfTmp$lam)
  tn<-sum(na.rm=TRUE,(1-refTmp)*(1-dfTmp$lam))
  fn<-sum(na.rm=TRUE,refTmp*(1-dfTmp$lam))
  dfPerf$lam_sens[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tp/(tp+fn))),"% (",tp,"/",tp+fn,")")
  dfPerf$lam_spec[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tn/(tn+fp))),"% (",tn,"/",tn+fp,")")
  
  #SSM
  tp<-sum(na.rm=TRUE,refTmp*dfTmp$ssm)
  fp<-sum(na.rm=TRUE,(1-refTmp)*dfTmp$ssm)
  tn<-sum(na.rm=TRUE,(1-refTmp)*(1-dfTmp$ssm))
  fn<-sum(na.rm=TRUE,refTmp*(1-dfTmp$ssm))
  dfPerf$ssm_sens[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tp/(tp+fn))),"% (",tp,"/",tp+fn,")")
  dfPerf$ssm_spec[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tn/(tn+fp))),"% (",tn,"/",tn+fp,")")
  
  #XPERT POOLED
  tp<-sum(na.rm=TRUE,refTmp*dfTmp$poolxpert)
  fp<-sum(na.rm=TRUE,(1-refTmp)*dfTmp$poolxpert)
  tn<-sum(na.rm=TRUE,(1-refTmp)*(1-dfTmp$poolxpert))
  fn<-sum(na.rm=TRUE,refTmp*(1-dfTmp$poolxpert))
  dfPerf$poolxpert_sens[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tp/(tp+fn))),"% (",tp,"/",tp+fn,")")
  dfPerf$poolxpert_spec[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tn/(tn+fp))),"% (",tn,"/",tn+fp,")")
  
  #XPERT SINGLE
  tp<-sum(na.rm=TRUE,refTmp*dfTmp$xpert)
  fp<-sum(na.rm=TRUE,(1-refTmp)*dfTmp$xpert)
  tn<-sum(na.rm=TRUE,(1-refTmp)*(1-dfTmp$xpert))
  fn<-sum(na.rm=TRUE,refTmp*(1-dfTmp$xpert))
  dfPerf$xpert_sens[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tp/(tp+fn))),"% (",tp,"/",tp+fn,")")
  dfPerf$xpert_spec[j]<-paste(sep="",format(nsmall=1,round(digits=1,100*tn/(tn+fp))),"% (",tn,"/",tn+fp,")")
  
  rm(list=c("dfTmp","tp","tn","fp","fn"))
}

dfPerf$CRS<-gsub(dfPerf$CRS,pattern="_+",replacement=" ")

dfPerf<-dfPerf[!grepl(pattern="NaN",dfPerf$Prevalence),]
dfPerf<-dfPerf[dfPerf$cultNegXpertNeg+dfPerf$cultPosXpertNeg+dfPerf$cultNegXpertPos+dfPerf$cultPosXpertPos>=5,]

for(i in 1:nrow(dfPerf)){
  for(j in 1:ncol(dfPerf)){
    if(grepl(pattern="NaN",dfPerf[i,j])){dfPerf[i,j]<-NA}
  }
}

dfPerf %>%
  dplyr::select(!c(Country,Region,Setting,CRS)) %>%
  kable(row.names = FALSE,col.names=c("Reference","Population","TB prevalence","-/-","+/-","-/+","+/+",rep(c("sensitivity","specifity"),6))) %>%
  add_header_above(c(" "=1," "=1," "=1,"Culture/Xpert"=4,"CXR"=2,"CRP"=2,"LAM"=2,"SSM"=2,"Pooled Xpert"=2,"Xpert Ultra"=2)) %>%
  kableExtra::group_rows(index=table(fct_inorder(dfPerf$CRS))) %>%
  kable_styling(full_width=FALSE)
Table 9: Summary of the performance of individual tests compared against culture and Xpert Ultra.
Culture/Xpert
CXR
CRP
LAM
SSM
Pooled Xpert
Xpert Ultra
Reference Population TB prevalence -/- +/- -/+ +/+ sensitivity specifity sensitivity specifity sensitivity specifity sensitivity specifity sensitivity specifity sensitivity specifity
Cameroon PHC
culture All 7.9% (79/1000) 916 1 5 78 86.1% (68/79) 86.4% (796/921) 84.8% (67/79) 94.2% (868/921) 82.3% (65/79) 89.0% (820/921) 65.8% (52/79) 96.2% (886/921) 93.7% (74/79) 95.0% (875/921) 98.7% (78/79) 99.5% (916/921)
xpert All 8.3% (83/1000) 916 1 5 78 81.9% (68/83) 86.4% (792/917) 80.7% (67/83) 94.2% (864/917) 78.3% (65/83) 89.0% (816/917) 61.4% (51/83) 96.1% (881/917) 90.4% (75/83) 95.1% (872/917) 100.0% (83/83) 100.0% (917/917)
culture PLWithHIV 11.6% (50/431) 380 1 1 49 88.0% (44/50) 85.0% (324/381) 84.0% (42/50) 92.1% (351/381) 84.0% (42/50) 88.7% (338/381) 68.0% (34/50) 95.8% (365/381) 90.0% (45/50) 96.3% (367/381) 98.0% (49/50) 99.7% (380/381)
xpert PLWithHIV 11.6% (50/431) 380 1 1 49 88.0% (44/50) 85.0% (324/381) 82.0% (41/50) 91.9% (350/381) 84.0% (42/50) 88.7% (338/381) 66.0% (33/50) 95.5% (364/381) 90.0% (45/50) 96.3% (367/381) 100.0% (50/50) 100.0% (381/381)
culture PLWithoutHIV 5.1% (29/569) 536 0 4 29 82.8% (24/29) 87.4% (472/540) 86.2% (25/29) 95.7% (517/540) 79.3% (23/29) 89.3% (482/540) 62.1% (18/29) 96.5% (521/540) 100.0% (29/29) 94.1% (508/540) 100.0% (29/29) 99.3% (536/540)
xpert PLWithoutHIV 5.8% (33/569) 536 0 4 29 72.7% (24/33) 87.3% (468/536) 78.8% (26/33) 95.9% (514/536) 69.7% (23/33) 89.2% (478/536) 54.5% (18/33) 96.5% (517/536) 90.9% (30/33) 94.2% (505/536) 100.0% (33/33) 100.0% (536/536)
Cameroon District
culture All 8.5% (85/1000) 907 3 8 82 85.9% (73/85) 88.3% (808/915) 92.9% (79/85) 94.6% (866/915) 81.2% (69/85) 88.4% (809/915) 69.4% (59/85) 96.7% (885/915) 90.6% (77/85) 95.4% (873/915) 96.5% (82/85) 99.1% (907/915)
xpert All 9.0% (90/1000) 907 3 8 82 78.9% (71/90) 88.0% (801/910) 84.4% (76/90) 94.3% (858/910) 75.6% (68/90) 88.2% (803/910) 64.4% (58/90) 96.6% (879/910) 82.2% (74/90) 95.1% (865/910) 100.0% (90/90) 100.0% (910/910)
culture PLWithHIV 14.2% (63/445) 380 3 2 60 84.1% (53/63) 88.0% (336/382) 93.7% (59/63) 95.3% (364/382) 82.5% (52/63) 87.4% (334/382) 68.3% (43/63) 96.9% (370/382) 88.9% (56/63) 95.0% (363/382) 95.2% (60/63) 99.5% (380/382)
xpert PLWithHIV 13.9% (62/445) 380 3 2 60 80.6% (50/62) 87.2% (334/383) 90.3% (56/62) 94.5% (362/383) 79.0% (49/62) 86.7% (332/383) 67.7% (42/62) 96.6% (370/383) 85.5% (53/62) 94.3% (361/383) 100.0% (62/62) 100.0% (383/383)
culture PLWithoutHIV 4.0% (22/555) 527 0 6 22 90.9% (20/22) 88.6% (472/533) 90.9% (20/22) 94.2% (502/533) 77.3% (17/22) 89.1% (475/533) 72.7% (16/22) 96.6% (515/533) 95.5% (21/22) 95.7% (510/533) 100.0% (22/22) 98.9% (527/533)
xpert PLWithoutHIV 5.0% (28/555) 527 0 6 22 75.0% (21/28) 88.6% (467/527) 71.4% (20/28) 94.1% (496/527) 67.9% (19/28) 89.4% (471/527) 57.1% (16/28) 96.6% (509/527) 75.0% (21/28) 95.6% (504/527) 100.0% (28/28) 100.0% (527/527)
Cameroon Nomads
culture All 3.8% (30/800) 763 1 7 29 93.3% (28/30) 86.4% (665/770) 83.3% (25/30) 95.7% (737/770) 80.0% (24/30) 88.7% (683/770) 63.3% (19/30) 95.8% (738/770) 96.7% (29/30) 95.8% (738/770) 96.7% (29/30) 99.1% (763/770)
xpert All 4.5% (36/800) 763 1 7 29 83.3% (30/36) 86.5% (661/764) 66.7% (24/36) 95.5% (730/764) 66.7% (24/36) 88.6% (677/764) 50.0% (18/36) 95.7% (731/764) 80.6% (29/36) 95.8% (732/764) 100.0% (36/36) 100.0% (764/764)
culture PLWithHIV 6.4% (21/328) 304 0 3 21 95.2% (20/21) 86.6% (266/307) 81.0% (17/21) 94.8% (291/307) 76.2% (16/21) 87.0% (267/307) 61.9% (13/21) 95.1% (292/307) 100.0% (21/21) 96.1% (295/307) 100.0% (21/21) 99.0% (304/307)
xpert PLWithHIV 7.3% (24/328) 304 0 3 21 87.5% (21/24) 86.8% (264/304) 70.8% (17/24) 94.7% (288/304) 70.8% (17/24) 87.2% (265/304) 54.2% (13/24) 95.1% (289/304) 87.5% (21/24) 96.1% (292/304) 100.0% (24/24) 100.0% (304/304)
culture PLWithoutHIV 1.9% (9/472) 459 1 4 8 88.9% (8/9) 86.2% (399/463) 88.9% (8/9) 96.3% (446/463) 88.9% (8/9) 89.8% (416/463) 66.7% (6/9) 96.3% (446/463) 88.9% (8/9) 95.7% (443/463) 88.9% (8/9) 99.1% (459/463)
xpert PLWithoutHIV 2.5% (12/472) 459 1 4 8 75.0% (9/12) 86.3% (397/460) 58.3% (7/12) 96.1% (442/460) 58.3% (7/12) 89.6% (412/460) 41.7% (5/12) 96.1% (442/460) 66.7% (8/12) 95.7% (440/460) 100.0% (12/12) 100.0% (460/460)
Cameroon Children
culture All 3.4% (17/500) 480 0 3 17 88.2% (15/17) 85.7% (414/483) 76.5% (13/17) 96.1% (464/483) 88.2% (15/17) 89.6% (433/483) 64.7% (11/17) 95.7% (462/483) 94.1% (16/17) 97.1% (469/483) 100.0% (17/17) 99.4% (480/483)
xpert All 4.0% (20/500) 480 0 3 17 75.0% (15/20) 85.6% (411/480) 70.0% (14/20) 96.2% (462/480) 75.0% (15/20) 89.6% (430/480) 55.0% (11/20) 95.6% (459/480) 80.0% (16/20) 97.1% (466/480) 100.0% (20/20) 100.0% (480/480)
culture PLWithHIV 4.3% (9/210) 200 0 1 9 77.8% (7/9) 87.6% (176/201) 88.9% (8/9) 95.5% (192/201) 77.8% (7/9) 91.0% (183/201) 66.7% (6/9) 95.5% (192/201) 88.9% (8/9) 97.5% (196/201) 100.0% (9/9) 99.5% (200/201)
xpert PLWithHIV 4.8% (10/210) 200 0 1 9 70.0% (7/10) 87.5% (175/200) 80.0% (8/10) 95.5% (191/200) 70.0% (7/10) 91.0% (182/200) 60.0% (6/10) 95.5% (191/200) 80.0% (8/10) 97.5% (195/200) 100.0% (10/10) 100.0% (200/200)
culture PLWithoutHIV 2.8% (8/290) 280 0 2 8 100.0% (8/8) 84.4% (238/282) 62.5% (5/8) 96.5% (272/282) 100.0% (8/8) 88.7% (250/282) 62.5% (5/8) 95.7% (270/282) 100.0% (8/8) 96.8% (273/282) 100.0% (8/8) 99.3% (280/282)
xpert PLWithoutHIV 3.4% (10/290) 280 0 2 8 80.0% (8/10) 84.3% (236/280) 60.0% (6/10) 96.8% (271/280) 80.0% (8/10) 88.6% (248/280) 50.0% (5/10) 95.7% (268/280) 80.0% (8/10) 96.8% (271/280) 100.0% (10/10) 100.0% (280/280)
Nigeria ZRC PHC
culture All 21.9% (153/700) 540 5 7 148 88.9% (136/153) 88.3% (483/547) 96.1% (147/153) 96.9% (530/547) 81.0% (124/153) 88.7% (485/547) 68.6% (105/153) 95.6% (523/547) 92.2% (141/153) 98.4% (538/547) 96.7% (148/153) 98.7% (540/547)
xpert All 22.1% (155/700) 540 5 7 148 85.8% (133/155) 87.7% (478/545) 92.3% (143/155) 96.1% (524/545) 77.4% (120/155) 87.9% (479/545) 65.8% (102/155) 95.0% (518/545) 89.0% (138/155) 97.8% (533/545) 100.0% (155/155) 100.0% (545/545)
culture PLWithHIV 34.3% (113/329) 215 3 1 110 89.4% (101/113) 88.9% (192/216) 96.5% (109/113) 97.7% (211/216) 79.6% (90/113) 90.7% (196/216) 69.9% (79/113) 95.8% (207/216) 92.9% (105/113) 99.1% (214/216) 97.3% (110/113) 99.5% (215/216)
xpert PLWithHIV 33.7% (111/329) 215 3 1 110 89.2% (99/111) 88.1% (192/218) 95.5% (106/111) 96.3% (210/218) 78.4% (87/111) 89.4% (195/218) 69.4% (77/111) 95.0% (207/218) 92.8% (103/111) 98.2% (214/218) 100.0% (111/111) 100.0% (218/218)
culture PLWithoutHIV 10.8% (40/371) 325 2 6 38 87.5% (35/40) 87.9% (291/331) 95.0% (38/40) 96.4% (319/331) 85.0% (34/40) 87.3% (289/331) 65.0% (26/40) 95.5% (316/331) 90.0% (36/40) 97.9% (324/331) 95.0% (38/40) 98.2% (325/331)
xpert PLWithoutHIV 11.9% (44/371) 325 2 6 38 77.3% (34/44) 87.5% (286/327) 84.1% (37/44) 96.0% (314/327) 75.0% (33/44) 86.9% (284/327) 56.8% (25/44) 95.1% (311/327) 79.5% (35/44) 97.6% (319/327) 100.0% (44/44) 100.0% (327/327)
Nigeria ZRC District
culture All 17.0% (119/700) 576 2 5 117 91.6% (109/119) 86.1% (500/581) 92.4% (110/119) 93.1% (541/581) 79.0% (94/119) 88.6% (515/581) 64.7% (77/119) 95.5% (555/581) 90.8% (108/119) 97.2% (565/581) 98.3% (117/119) 99.1% (576/581)
xpert All 17.4% (122/700) 576 2 5 117 89.3% (109/122) 86.0% (497/578) 88.5% (108/122) 92.7% (536/578) 76.2% (93/122) 88.4% (511/578) 63.1% (77/122) 95.5% (552/578) 86.9% (106/122) 96.9% (560/578) 100.0% (122/122) 100.0% (578/578)
culture PLWithHIV 22.5% (73/324) 251 2 0 71 94.5% (69/73) 86.5% (217/251) 90.4% (66/73) 94.4% (237/251) 76.7% (56/73) 90.0% (226/251) 60.3% (44/73) 94.4% (237/251) 90.4% (66/73) 98.4% (247/251) 97.3% (71/73) 100.0% (251/251)
xpert PLWithHIV 21.9% (71/324) 251 2 0 71 94.4% (67/71) 85.8% (217/253) 90.1% (64/71) 93.7% (237/253) 76.1% (54/71) 89.3% (226/253) 62.0% (44/71) 94.5% (239/253) 90.1% (64/71) 97.6% (247/253) 100.0% (71/71) 100.0% (253/253)
culture PLWithoutHIV 12.2% (46/376) 325 0 5 46 87.0% (40/46) 85.8% (283/330) 95.7% (44/46) 92.1% (304/330) 82.6% (38/46) 87.6% (289/330) 71.7% (33/46) 96.4% (318/330) 91.3% (42/46) 96.4% (318/330) 100.0% (46/46) 98.5% (325/330)
xpert PLWithoutHIV 13.6% (51/376) 325 0 5 46 82.4% (42/51) 86.2% (280/325) 86.3% (44/51) 92.0% (299/325) 76.5% (39/51) 87.7% (285/325) 64.7% (33/51) 96.3% (313/325) 82.4% (42/51) 96.3% (313/325) 100.0% (51/51) 100.0% (325/325)
Nigeria JHF Nomads
culture All 7.7% (77/1000) 909 1 14 76 89.6% (69/77) 88.4% (816/923) 94.8% (73/77) 96.3% (889/923) 85.7% (66/77) 88.1% (813/923) 72.7% (56/77) 95.2% (879/923) 88.3% (68/77) 97.0% (895/923) 98.7% (76/77) 98.5% (909/923)
xpert All 9.0% (90/1000) 909 1 14 76 80.0% (72/90) 88.6% (806/910) 80.0% (72/90) 96.2% (875/910) 72.2% (65/90) 87.8% (799/910) 63.3% (57/90) 95.3% (867/910) 75.6% (68/90) 96.9% (882/910) 100.0% (90/90) 100.0% (910/910)
culture PLWithHIV 11.3% (49/432) 378 1 5 48 89.8% (44/49) 88.3% (338/383) 95.9% (47/49) 96.1% (368/383) 87.8% (43/49) 86.4% (331/383) 71.4% (35/49) 95.6% (366/383) 83.7% (41/49) 95.0% (364/383) 98.0% (48/49) 98.7% (378/383)
xpert PLWithHIV 12.3% (53/432) 378 1 5 48 84.9% (45/53) 88.4% (335/379) 86.8% (46/53) 95.8% (363/379) 79.2% (42/53) 86.0% (326/379) 66.0% (35/53) 95.5% (362/379) 77.4% (41/53) 95.0% (360/379) 100.0% (53/53) 100.0% (379/379)
culture PLWithoutHIV 4.9% (28/568) 531 0 9 28 89.3% (25/28) 88.5% (478/540) 92.9% (26/28) 96.5% (521/540) 82.1% (23/28) 89.3% (482/540) 75.0% (21/28) 95.0% (513/540) 96.4% (27/28) 98.3% (531/540) 100.0% (28/28) 98.3% (531/540)
xpert PLWithoutHIV 6.5% (37/568) 531 0 9 28 73.0% (27/37) 88.7% (471/531) 70.3% (26/37) 96.4% (512/531) 62.2% (23/37) 89.1% (473/531) 59.5% (22/37) 95.1% (505/531) 73.0% (27/37) 98.3% (522/531) 100.0% (37/37) 100.0% (531/531)
Nigeria JHF IDP / refugees
culture All 11.7% (117/1000) 875 2 8 115 87.2% (102/117) 89.7% (792/883) 92.3% (108/117) 94.5% (834/883) 82.1% (96/117) 88.6% (782/883) 82.9% (97/117) 96.5% (852/883) 86.3% (101/117) 98.0% (865/883) 98.3% (115/117) 99.1% (875/883)
xpert All 12.3% (123/1000) 875 2 8 115 81.3% (100/123) 89.4% (784/877) 86.2% (106/123) 94.2% (826/877) 77.2% (95/123) 88.4% (775/877) 77.2% (95/123) 96.2% (844/877) 81.3% (100/123) 97.8% (858/877) 100.0% (123/123) 100.0% (877/877)
culture PLWithHIV 18.0% (77/427) 348 2 2 75 85.7% (66/77) 88.6% (310/350) 90.9% (70/77) 93.7% (328/350) 80.5% (62/77) 89.7% (314/350) 84.4% (65/77) 96.3% (337/350) 87.0% (67/77) 98.0% (343/350) 97.4% (75/77) 99.4% (348/350)
xpert PLWithHIV 18.0% (77/427) 348 2 2 75 83.1% (64/77) 88.0% (308/350) 88.3% (68/77) 93.1% (326/350) 77.9% (60/77) 89.1% (312/350) 81.8% (63/77) 95.7% (335/350) 84.4% (65/77) 97.4% (341/350) 100.0% (77/77) 100.0% (350/350)
culture PLWithoutHIV 7.0% (40/573) 527 0 6 40 90.0% (36/40) 90.4% (482/533) 95.0% (38/40) 94.9% (506/533) 85.0% (34/40) 87.8% (468/533) 80.0% (32/40) 96.6% (515/533) 85.0% (34/40) 97.9% (522/533) 100.0% (40/40) 98.9% (527/533)
xpert PLWithoutHIV 8.0% (46/573) 527 0 6 40 78.3% (36/46) 90.3% (476/527) 82.6% (38/46) 94.9% (500/527) 76.1% (35/46) 87.9% (463/527) 69.6% (32/46) 96.6% (509/527) 76.1% (35/46) 98.1% (517/527) 100.0% (46/46) 100.0% (527/527)
Kenya PHC
culture All 8.4% (59/700) 633 0 8 59 79.7% (47/59) 87.8% (563/641) 89.8% (53/59) 94.5% (606/641) 81.4% (48/59) 87.8% (563/641) 69.5% (41/59) 96.1% (616/641) 91.5% (54/59) 97.0% (622/641) 100.0% (59/59) 98.8% (633/641)
xpert All 9.6% (67/700) 633 0 8 59 70.1% (47/67) 87.7% (555/633) 82.1% (55/67) 94.8% (600/633) 74.6% (50/67) 88.0% (557/633) 61.2% (41/67) 96.1% (608/633) 82.1% (55/67) 97.2% (615/633) 100.0% (67/67) 100.0% (633/633)
culture PLWithHIV 12.1% (38/315) 275 0 2 38 73.7% (28/38) 91.0% (252/277) 86.8% (33/38) 94.2% (261/277) 78.9% (30/38) 87.7% (243/277) 60.5% (23/38) 96.0% (266/277) 89.5% (34/38) 97.8% (271/277) 100.0% (38/38) 99.3% (275/277)
xpert PLWithHIV 12.7% (40/315) 275 0 2 38 70.0% (28/40) 90.9% (250/275) 85.0% (34/40) 94.5% (260/275) 77.5% (31/40) 88.0% (242/275) 57.5% (23/40) 96.0% (264/275) 85.0% (34/40) 97.8% (269/275) 100.0% (40/40) 100.0% (275/275)
culture PLWithoutHIV 5.5% (21/385) 358 0 6 21 90.5% (19/21) 85.4% (311/364) 95.2% (20/21) 94.8% (345/364) 85.7% (18/21) 87.9% (320/364) 85.7% (18/21) 96.2% (350/364) 95.2% (20/21) 96.4% (351/364) 100.0% (21/21) 98.4% (358/364)
xpert PLWithoutHIV 7.0% (27/385) 358 0 6 21 70.4% (19/27) 85.2% (305/358) 77.8% (21/27) 95.0% (340/358) 70.4% (19/27) 88.0% (315/358) 66.7% (18/27) 96.1% (344/358) 77.8% (21/27) 96.6% (346/358) 100.0% (27/27) 100.0% (358/358)
Kenya District
culture All 19.7% (138/700) 550 3 12 135 93.5% (129/138) 87.5% (492/562) 87.7% (121/138) 96.6% (543/562) 81.2% (112/138) 88.3% (496/562) 72.5% (100/138) 95.2% (535/562) 91.3% (126/138) 95.7% (538/562) 97.8% (135/138) 97.9% (550/562)
xpert All 21.0% (147/700) 550 3 12 135 86.4% (127/147) 87.0% (481/553) 81.0% (119/147) 96.2% (532/553) 74.8% (110/147) 87.7% (485/553) 66.0% (97/147) 94.6% (523/553) 84.4% (124/147) 95.3% (527/553) 100.0% (147/147) 100.0% (553/553)
culture PLWithHIV 30.0% (99/330) 226 2 5 97 91.9% (91/99) 86.1% (199/231) 87.9% (87/99) 97.0% (224/231) 78.8% (78/99) 87.4% (202/231) 69.7% (69/99) 93.9% (217/231) 89.9% (89/99) 96.5% (223/231) 98.0% (97/99) 97.8% (226/231)
xpert PLWithHIV 30.9% (102/330) 226 2 5 97 88.2% (90/102) 85.5% (195/228) 84.3% (86/102) 96.5% (220/228) 74.5% (76/102) 86.4% (197/228) 65.7% (67/102) 93.0% (212/228) 85.3% (87/102) 95.6% (218/228) 100.0% (102/102) 100.0% (228/228)
culture PLWithoutHIV 10.5% (39/370) 324 1 7 38 97.4% (38/39) 88.5% (293/331) 87.2% (34/39) 96.4% (319/331) 87.2% (34/39) 88.8% (294/331) 79.5% (31/39) 96.1% (318/331) 94.9% (37/39) 95.2% (315/331) 97.4% (38/39) 97.9% (324/331)
xpert PLWithoutHIV 12.2% (45/370) 324 1 7 38 82.2% (37/45) 88.0% (286/325) 73.3% (33/45) 96.0% (312/325) 75.6% (34/45) 88.6% (288/325) 66.7% (30/45) 95.7% (311/325) 82.2% (37/45) 95.1% (309/325) 100.0% (45/45) 100.0% (325/325)
Bangladesh PHC
culture All 3.4% (34/1000) 954 0 12 34 97.1% (33/34) 89.8% (867/966) 88.2% (30/34) 95.2% (920/966) 82.4% (28/34) 91.7% (886/966) 76.5% (26/34) 93.4% (902/966) 85.3% (29/34) 96.8% (935/966) 100.0% (34/34) 98.8% (954/966)
xpert All 4.6% (46/1000) 954 0 12 34 73.9% (34/46) 89.7% (856/954) 65.2% (30/46) 95.2% (908/954) 60.9% (28/46) 91.6% (874/954) 58.7% (27/46) 93.4% (891/954) 63.0% (29/46) 96.8% (923/954) 100.0% (46/46) 100.0% (954/954)
culture PLWithHIV 6.6% (26/391) 360 0 5 26 96.2% (25/26) 88.8% (324/365) 84.6% (22/26) 94.0% (343/365) 76.9% (20/26) 92.3% (337/365) 73.1% (19/26) 92.3% (337/365) 88.5% (23/26) 97.0% (354/365) 100.0% (26/26) 98.6% (360/365)
xpert PLWithHIV 7.9% (31/391) 360 0 5 26 80.6% (25/31) 88.6% (319/360) 71.0% (22/31) 93.9% (338/360) 64.5% (20/31) 92.2% (332/360) 61.3% (19/31) 92.2% (332/360) 74.2% (23/31) 96.9% (349/360) 100.0% (31/31) 100.0% (360/360)
culture PLWithoutHIV 1.3% (8/609) 594 0 7 8 100.0% (8/8) 90.3% (543/601) 100.0% (8/8) 96.0% (577/601) 100.0% (8/8) 91.3% (549/601) 87.5% (7/8) 94.0% (565/601) 75.0% (6/8) 96.7% (581/601) 100.0% (8/8) 98.8% (594/601)
xpert PLWithoutHIV 2.5% (15/609) 594 0 7 8 60.0% (9/15) 90.4% (537/594) 53.3% (8/15) 96.0% (570/594) 53.3% (8/15) 91.2% (542/594) 53.3% (8/15) 94.1% (559/594) 40.0% (6/15) 96.6% (574/594) 100.0% (15/15) 100.0% (594/594)
Bangladesh District
culture All 2.5% (25/1000) 961 0 14 25 84.0% (21/25) 92.0% (897/975) 88.0% (22/25) 95.5% (931/975) 92.0% (23/25) 89.6% (874/975) 64.0% (16/25) 94.4% (920/975) 84.0% (21/25) 97.1% (947/975) 100.0% (25/25) 98.6% (961/975)
xpert All 3.9% (39/1000) 961 0 14 25 56.4% (22/39) 92.0% (884/961) 59.0% (23/39) 95.5% (918/961) 59.0% (23/39) 89.5% (860/961) 41.0% (16/39) 94.3% (906/961) 56.4% (22/39) 97.2% (934/961) 100.0% (39/39) 100.0% (961/961)
culture PLWithHIV 4.3% (17/399) 375 0 7 17 82.4% (14/17) 91.1% (348/382) 88.2% (15/17) 95.0% (363/382) 94.1% (16/17) 92.1% (352/382) 70.6% (12/17) 95.3% (364/382) 76.5% (13/17) 97.6% (373/382) 100.0% (17/17) 98.2% (375/382)
xpert PLWithHIV 6.0% (24/399) 375 0 7 17 62.5% (15/24) 91.2% (342/375) 66.7% (16/24) 95.2% (357/375) 66.7% (16/24) 92.0% (345/375) 50.0% (12/24) 95.2% (357/375) 54.2% (13/24) 97.6% (366/375) 100.0% (24/24) 100.0% (375/375)
culture PLWithoutHIV 1.3% (8/601) 586 0 7 8 87.5% (7/8) 92.6% (549/593) 87.5% (7/8) 95.8% (568/593) 87.5% (7/8) 88.0% (522/593) 50.0% (4/8) 93.8% (556/593) 100.0% (8/8) 96.8% (574/593) 100.0% (8/8) 98.8% (586/593)
xpert PLWithoutHIV 2.5% (15/601) 586 0 7 8 46.7% (7/15) 92.5% (542/586) 46.7% (7/15) 95.7% (561/586) 46.7% (7/15) 87.9% (515/586) 26.7% (4/15) 93.7% (549/586) 60.0% (9/15) 96.9% (568/586) 100.0% (15/15) 100.0% (586/586)
Bangladesh Informal settlements
culture All 4.9% (49/1000) 940 1 11 48 85.7% (42/49) 91.4% (869/951) 77.6% (38/49) 95.4% (907/951) 81.6% (40/49) 92.3% (878/951) 75.5% (37/49) 94.0% (894/951) 89.8% (44/49) 96.2% (915/951) 98.0% (48/49) 98.8% (940/951)
xpert All 5.9% (59/1000) 940 1 11 48 71.2% (42/59) 91.3% (859/941) 66.1% (39/59) 95.4% (898/941) 69.5% (41/59) 92.3% (869/941) 66.1% (39/59) 94.2% (886/941) 72.9% (43/59) 96.1% (904/941) 100.0% (59/59) 100.0% (941/941)
culture PLWithHIV 8.6% (36/419) 379 0 4 36 88.9% (32/36) 92.4% (354/383) 75.0% (27/36) 94.5% (362/383) 83.3% (30/36) 93.7% (359/383) 83.3% (30/36) 93.2% (357/383) 97.2% (35/36) 96.1% (368/383) 100.0% (36/36) 99.0% (379/383)
xpert PLWithHIV 9.5% (40/419) 379 0 4 36 82.5% (33/40) 92.6% (351/379) 70.0% (28/40) 94.7% (359/379) 77.5% (31/40) 93.9% (356/379) 75.0% (30/40) 93.1% (353/379) 87.5% (35/40) 96.0% (364/379) 100.0% (40/40) 100.0% (379/379)
culture PLWithoutHIV 2.2% (13/581) 561 1 7 12 76.9% (10/13) 90.7% (515/568) 84.6% (11/13) 96.0% (545/568) 76.9% (10/13) 91.4% (519/568) 53.8% (7/13) 94.5% (537/568) 69.2% (9/13) 96.3% (547/568) 92.3% (12/13) 98.8% (561/568)
xpert PLWithoutHIV 3.3% (19/581) 561 1 7 12 47.4% (9/19) 90.4% (508/562) 57.9% (11/19) 95.9% (539/562) 52.6% (10/19) 91.3% (513/562) 47.4% (9/19) 94.8% (533/562) 42.1% (8/19) 96.1% (540/562) 100.0% (19/19) 100.0% (562/562)
Brazil Aracaju PHC
culture All 14.8% (74/500) 425 2 1 72 93.2% (69/74) 84.3% (359/426) 82.4% (61/74) 97.9% (417/426) 83.8% (62/74) 91.1% (388/426) 66.2% (49/74) 95.5% (407/426) 91.9% (68/74) 97.7% (416/426) 97.3% (72/74) 99.8% (425/426)
xpert All 14.6% (73/500) 425 2 1 72 91.8% (67/73) 83.8% (358/427) 83.6% (61/73) 97.9% (418/427) 83.6% (61/73) 90.9% (388/427) 65.8% (48/73) 95.3% (407/427) 91.8% (67/73) 97.4% (416/427) 100.0% (73/73) 100.0% (427/427)
culture PLWithHIV 21.8% (48/220) 171 2 1 46 89.6% (43/48) 83.7% (144/172) 77.1% (37/48) 97.7% (168/172) 85.4% (41/48) 91.9% (158/172) 64.6% (31/48) 95.3% (164/172) 91.7% (44/48) 97.7% (168/172) 95.8% (46/48) 99.4% (171/172)
xpert PLWithHIV 21.4% (47/220) 171 2 1 46 87.2% (41/47) 82.7% (143/173) 78.7% (37/47) 97.7% (169/173) 85.1% (40/47) 91.3% (158/173) 63.8% (30/47) 94.8% (164/173) 91.5% (43/47) 97.1% (168/173) 100.0% (47/47) 100.0% (173/173)
culture PLWithoutHIV 9.3% (26/280) 254 0 0 26 100.0% (26/26) 84.6% (215/254) 92.3% (24/26) 98.0% (249/254) 80.8% (21/26) 90.6% (230/254) 69.2% (18/26) 95.7% (243/254) 92.3% (24/26) 97.6% (248/254) 100.0% (26/26) 100.0% (254/254)
xpert PLWithoutHIV 9.3% (26/280) 254 0 0 26 100.0% (26/26) 84.6% (215/254) 92.3% (24/26) 98.0% (249/254) 80.8% (21/26) 90.6% (230/254) 69.2% (18/26) 95.7% (243/254) 92.3% (24/26) 97.6% (248/254) 100.0% (26/26) 100.0% (254/254)
Brazil Maceio PHC
culture All 28.2% (141/500) 357 1 2 140 85.8% (121/141) 90.5% (325/359) 86.5% (122/141) 96.7% (347/359) 77.3% (109/141) 88.6% (318/359) 63.1% (89/141) 93.9% (337/359) 92.2% (130/141) 97.5% (350/359) 99.3% (140/141) 99.4% (357/359)
xpert All 28.4% (142/500) 357 1 2 140 84.5% (120/142) 90.2% (323/358) 85.9% (122/142) 96.6% (346/358) 76.1% (108/142) 88.3% (316/358) 62.0% (88/142) 93.6% (335/358) 90.8% (129/142) 97.2% (348/358) 100.0% (142/142) 100.0% (358/358)
culture PLWithHIV 44.2% (103/233) 129 1 1 102 84.5% (87/103) 90.0% (117/130) 88.3% (91/103) 96.2% (125/130) 75.7% (78/103) 89.2% (116/130) 65.0% (67/103) 96.2% (125/130) 94.2% (97/103) 99.2% (129/130) 99.0% (102/103) 99.2% (129/130)
xpert PLWithHIV 44.2% (103/233) 129 1 1 102 83.5% (86/103) 89.2% (116/130) 88.3% (91/103) 96.2% (125/130) 74.8% (77/103) 88.5% (115/130) 64.1% (66/103) 95.4% (124/130) 93.2% (96/103) 98.5% (128/130) 100.0% (103/103) 100.0% (130/130)
culture PLWithoutHIV 14.2% (38/267) 228 0 1 38 89.5% (34/38) 90.8% (208/229) 81.6% (31/38) 96.9% (222/229) 81.6% (31/38) 88.2% (202/229) 57.9% (22/38) 92.6% (212/229) 86.8% (33/38) 96.5% (221/229) 100.0% (38/38) 99.6% (228/229)
xpert PLWithoutHIV 14.6% (39/267) 228 0 1 38 87.2% (34/39) 90.8% (207/228) 79.5% (31/39) 96.9% (221/228) 79.5% (31/39) 88.2% (201/228) 56.4% (22/39) 92.5% (211/228) 84.6% (33/39) 96.5% (220/228) 100.0% (39/39) 100.0% (228/228)
Viet Nam District
culture All 1.0% (10/1000) 979 0 11 10 90.0% (9/10) 92.1% (912/990) 100.0% (10/10) 95.3% (943/990) 80.0% (8/10) 89.8% (889/990) 80.0% (8/10) 93.6% (927/990) 90.0% (9/10) 97.0% (960/990) 100.0% (10/10) 98.9% (979/990)
xpert All 2.1% (21/1000) 979 0 11 10 47.6% (10/21) 92.1% (902/979) 52.4% (11/21) 95.3% (933/979) 42.9% (9/21) 89.8% (879/979) 42.9% (9/21) 93.7% (917/979) 42.9% (9/21) 96.9% (949/979) 100.0% (21/21) 100.0% (979/979)
culture PLWithHIV 1.8% (7/398) 386 0 5 7 85.7% (6/7) 91.6% (358/391) 100.0% (7/7) 95.4% (373/391) 100.0% (7/7) 88.0% (344/391) 71.4% (5/7) 92.3% (361/391) 100.0% (7/7) 96.7% (378/391) 100.0% (7/7) 98.7% (386/391)
xpert PLWithHIV 3.0% (12/398) 386 0 5 7 50.0% (6/12) 91.5% (353/386) 66.7% (8/12) 95.6% (369/386) 66.7% (8/12) 88.1% (340/386) 41.7% (5/12) 92.2% (356/386) 58.3% (7/12) 96.6% (373/386) 100.0% (12/12) 100.0% (386/386)
culture PLWithoutHIV 0.5% (3/602) 593 0 6 3 100.0% (3/3) 92.5% (554/599) 100.0% (3/3) 95.2% (570/599) 33.3% (1/3) 91.0% (545/599) 100.0% (3/3) 94.5% (566/599) 66.7% (2/3) 97.2% (582/599) 100.0% (3/3) 99.0% (593/599)
xpert PLWithoutHIV 1.5% (9/602) 593 0 6 3 44.4% (4/9) 92.6% (549/593) 33.3% (3/9) 95.1% (564/593) 11.1% (1/9) 90.9% (539/593) 44.4% (4/9) 94.6% (561/593) 22.2% (2/9) 97.1% (576/593) 100.0% (9/9) 100.0% (593/593)
Viet Nam Informal settlements
culture All 0.7% (11/1500) 1472 0 17 11 100.0% (11/11) 91.9% (1368/1489) 100.0% (11/11) 94.2% (1403/1489) 72.7% (8/11) 91.1% (1356/1489) 63.6% (7/11) 94.7% (1410/1489) 100.0% (11/11) 96.6% (1438/1489) 100.0% (11/11) 98.9% (1472/1489)
xpert All 1.9% (28/1500) 1472 0 17 11 42.9% (12/28) 91.8% (1352/1472) 42.9% (12/28) 94.2% (1387/1472) 46.4% (13/28) 91.3% (1344/1472) 28.6% (8/28) 94.7% (1394/1472) 46.4% (13/28) 96.7% (1423/1472) 100.0% (28/28) 100.0% (1472/1472)
culture PLWithHIV 1.3% (8/603) 587 0 8 8 100.0% (8/8) 93.3% (555/595) 100.0% (8/8) 94.3% (561/595) 75.0% (6/8) 91.8% (546/595) 62.5% (5/8) 95.3% (567/595) 100.0% (8/8) 97.6% (581/595) 100.0% (8/8) 98.7% (587/595)
xpert PLWithHIV 2.7% (16/603) 587 0 8 8 56.2% (9/16) 93.4% (548/587) 56.2% (9/16) 94.4% (554/587) 50.0% (8/16) 92.0% (540/587) 31.2% (5/16) 95.2% (559/587) 56.2% (9/16) 97.8% (574/587) 100.0% (16/16) 100.0% (587/587)
culture PLWithoutHIV 0.3% (3/897) 885 0 9 3 100.0% (3/3) 90.9% (813/894) 100.0% (3/3) 94.2% (842/894) 66.7% (2/3) 90.6% (810/894) 66.7% (2/3) 94.3% (843/894) 100.0% (3/3) 95.9% (857/894) 100.0% (3/3) 99.0% (885/894)
xpert PLWithoutHIV 1.3% (12/897) 885 0 9 3 25.0% (3/12) 90.8% (804/885) 25.0% (3/12) 94.1% (833/885) 41.7% (5/12) 90.8% (804/885) 25.0% (3/12) 94.4% (835/885) 33.3% (4/12) 95.9% (849/885) 100.0% (12/12) 100.0% (885/885)
Viet Nam Children
culture All 0.0% (0/500) 496 0 4 0 . 92.2% (461/500) . 93.2% (466/500) . 90.4% (452/500) . 94.2% (471/500) . 97.0% (485/500) . 99.2% (496/500)
xpert All 0.8% (4/500) 496 0 4 0 25.0% (1/4) 92.3% (458/496) 0.0% (0/4) 93.1% (462/496) 25.0% (1/4) 90.5% (449/496) 0.0% (0/4) 94.2% (467/496) 25.0% (1/4) 97.2% (482/496) 100.0% (4/4) 100.0% (496/496)
culture PLWithHIV 0.0% (0/209) 206 0 3 0 . 94.3% (197/209) . 93.8% (196/209) . 90.0% (188/209) . 94.7% (198/209) . 96.7% (202/209) . 98.6% (206/209)
xpert PLWithHIV 1.4% (3/209) 206 0 3 0 33.3% (1/3) 94.7% (195/206) 0.0% (0/3) 93.7% (193/206) 33.3% (1/3) 90.3% (186/206) 0.0% (0/3) 94.7% (195/206) 33.3% (1/3) 97.1% (200/206) 100.0% (3/3) 100.0% (206/206)
culture PLWithoutHIV 0.0% (0/291) 290 0 1 0 . 90.7% (264/291) . 92.8% (270/291) . 90.7% (264/291) . 93.8% (273/291) . 97.3% (283/291) . 99.7% (290/291)
xpert PLWithoutHIV 0.3% (1/291) 290 0 1 0 0.0% (0/1) 90.7% (263/290) 0.0% (0/1) 92.8% (269/290) 0.0% (0/1) 90.7% (263/290) 0.0% (0/1) 93.8% (272/290) 0.0% (0/1) 97.2% (282/290) 100.0% (1/1) 100.0% (290/290)
Malawi PHC
culture All 7.8% (93/1200) 1096 1 11 92 90.3% (84/93) 91.3% (1011/1107) 91.4% (85/93) 96.2% (1065/1107) 81.7% (76/93) 91.9% (1017/1107) 71.0% (66/93) 94.5% (1046/1107) 90.3% (84/93) 97.7% (1082/1107) 98.9% (92/93) 99.0% (1096/1107)
xpert All 8.6% (103/1200) 1096 1 11 92 82.5% (85/103) 91.3% (1002/1097) 81.6% (84/103) 96.1% (1054/1097) 74.8% (77/103) 91.9% (1008/1097) 64.1% (66/103) 94.4% (1036/1097) 80.6% (83/103) 97.6% (1071/1097) 100.0% (103/103) 100.0% (1097/1097)
culture PLWithHIV 12.8% (65/507) 439 1 3 64 90.8% (59/65) 92.8% (410/442) 93.8% (61/65) 96.6% (427/442) 86.2% (56/65) 93.7% (414/442) 72.3% (47/65) 94.6% (418/442) 90.8% (59/65) 98.4% (435/442) 98.5% (64/65) 99.3% (439/442)
xpert PLWithHIV 13.2% (67/507) 439 1 3 64 86.6% (58/67) 92.5% (407/440) 89.6% (60/67) 96.4% (424/440) 82.1% (55/67) 93.4% (411/440) 68.7% (46/67) 94.3% (415/440) 86.6% (58/67) 98.2% (432/440) 100.0% (67/67) 100.0% (440/440)
culture PLWithoutHIV 4.0% (28/693) 657 0 8 28 89.3% (25/28) 90.4% (601/665) 85.7% (24/28) 95.9% (638/665) 71.4% (20/28) 90.7% (603/665) 67.9% (19/28) 94.4% (628/665) 89.3% (25/28) 97.3% (647/665) 100.0% (28/28) 98.8% (657/665)
xpert PLWithoutHIV 5.2% (36/693) 657 0 8 28 75.0% (27/36) 90.6% (595/657) 66.7% (24/36) 95.9% (630/657) 61.1% (22/36) 90.9% (597/657) 55.6% (20/36) 94.5% (621/657) 69.4% (25/36) 97.3% (639/657) 100.0% (36/36) 100.0% (657/657)

5.5.1.1 Fit the Bayesian models

This section only exists to hold the chunk of R code that fits the Bayesian models for all settings that we are considering. There is no other output.

Code
# prepare output data frame
dfResPrimBayes<-data.frame(
  country=c(rep("Cameroon",6),rep("Nigeria",8),rep("Kenya",2),rep("Bangladesh",5),rep("Brazil",2),rep("Viet Nam",5),rep("Malawi",1),"Aggregate","Aggregate"),
  region=c(rep("",6),c(rep("ZRC",2),rep("JHF",6)),rep("",2+5),"Aracaju","Maceio",rep("",5+1),rep("",2)),
  setting=c("PHC","District",rep("Nomads",3),"Children","PHC","District",rep("Nomads",3),rep("IDP / refugees",3),"PHC","District","PHC","District",rep("Informal settlements",3),"PHC","PHC","District",rep("Informal settlements",3),"Children","PHC","PHC","District"),
  entryToStudy=c(rep("W4SS",2),"W4SS","CXR","W4SS | CXR","",rep("W4SS",2),"W4SS","CXR","W4SS | CXR","W4SS","CXR","W4SS | CXR",rep("W4SS",2),rep("W4SS",2),"W4SS","CXR","W4SS | CXR",rep("W4SS",2),"W4SS","W4SS","CXR","W4SS | CXR","","W4SS",rep("W4SS",2))
) %>%
  dplyr::filter(setting!="Children")

allRes<-list()

printResults<-TRUE
checkW4ssFacility<-FALSE
checkW4ssAcf<-FALSE
checkW4ssCxrAcf<-FALSE
checkCxrAcf<-FALSE

outPrefix<-"mockData_AnalysisResults_"

for(i in 1:(nrow(dfResPrimBayes)-2)){
  print(i)
  
  if(dfResPrimBayes$setting[i] %in% c("PHC","District") & dfResPrimBayes$country[i]!="Aggregate"){
    res<-analysisFunBayesBetaPars(dat=dfSim,c=dfResPrimBayes$country[i],r=dfResPrimBayes$region[i],s=dfResPrimBayes$setting[i],doPlot=TRUE,plotFile=paste(sep="",outPrefix,"S4A_mcmcPars_",dfResPrimBayes$country[i],"_",dfResPrimBayes$region[i],"_",gsub(pattern=" / ",replacement="_",dfResPrimBayes$setting[i]),"_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile=jagsFileW4ss,parsVect=parsVectW4ssNoLR)
    
    # derive positive and negative likelihood ratios
    res$mcmcObj<-getLRpLRm(res$mcmcObj)
    
    # format the summary
    mcmcSumDf<-MCMCsummary(res$mcmcObj)
    mcmcSumDf$mean<-format(nsmall=3,round(digits=3,mcmcSumDf$mean))
    mcmcSumDf$sd<-format(nsmall=3,round(digits=3,mcmcSumDf$sd))
    mcmcSumDf$`2.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`2.5%`))
    mcmcSumDf$`50%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`50%`))
    mcmcSumDf$`97.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`97.5%`))
    mcmcSumDfPrim<-mcmcSumDf[parsVectPrim,]
    mcmcSumDf<-mcmcSumDf[parsVectW4ss,]

    
    # print the results
    if(printResults){
      mcmcSumDfPrim %>%
        kable(caption = paste(sep="","Summary of the MCMC parameter estimation (individual tests only) for country ",dfResPrimBayes$country[i],", region ",dfResPrimBayes$region[i],", setting ",dfResPrimBayes$setting[i]," (Facility-based screening set of diagnostic algorithms).\nMetrics shown are the posterior mean, standard deviation, 2.5th, 50th, 97.5th quantiles, the Gelman-Rubin potential scale reduction factor (Rhat) and the effective sample size (n.eff).")) %>%
        kable_styling(full_width=FALSE)
    }
    
    # add the summary to the result object
    res[["mcmcSummary"]]<-mcmcSumDf
    
    allRes[[paste(sep="_",dfResPrimBayes$country[i],dfResPrimBayes$region[i],dfResPrimBayes$setting[i])]]<-res
    
    values<-numeric(0)
    tmpParList<-parsVectW4ss[!grepl(pattern="lrp|lrm",parsVectW4ss)]
    for(j in 1:length(tmpParList)){
      parTmp<-unlist(strsplit(split="\\.",tmpParList[j]))
      if(length(parTmp)==1){
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"))
      }else{
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"),paste(collapse=".",parTmp[2:length(parTmp)]))
      }
      if(!(tmpParList[j] %in% res$betaPars$par)){cat(paste(sep="","Parameter ",tmpParList[j]," not found in MCMC parameter list.\n"))}
      outTmp<-unlist(res$betaPars[res$betaPars$par==tmpParList[j],c("a","b")])
      names(outTmp)<-parTmp.ab
      values<-c(values,outTmp)
    }
    
  }else if(dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$entryToStudy[i]=="W4SS" & dfResPrimBayes$country[i]!="Aggregate"){
    res<-analysisFunBayesBetaPars(dat=dfSim %>% filter(!is.na(w4ss) & w4ss==1),c=dfResPrimBayes$country[i],r=dfResPrimBayes$region[i],s=dfResPrimBayes$setting[i],doPlot=TRUE,plotFile=paste(sep="",outPrefix,"S4A_mcmcPars_",dfResPrimBayes$country[i],"_",dfResPrimBayes$region[i],"_",gsub(pattern=" / ",replacement="_",dfResPrimBayes$setting[i]),"_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile=jagsFileW4ss,parsVect=parsVectW4ssNoLR)
    
    # derive positive and negative likelihood ratios
    res$mcmcObj<-getLRpLRm(res$mcmcObj)
    
    # format the summary
    mcmcSumDf<-MCMCsummary(res$mcmcObj)
    mcmcSumDf$mean<-format(nsmall=3,round(digits=3,mcmcSumDf$mean))
    mcmcSumDf$sd<-format(nsmall=3,round(digits=3,mcmcSumDf$sd))
    mcmcSumDf$`2.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`2.5%`))
    mcmcSumDf$`50%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`50%`))
    mcmcSumDf$`97.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`97.5%`))
    mcmcSumDfPrim<-mcmcSumDf[parsVectPrim,]
    mcmcSumDf<-mcmcSumDf[parsVectW4ss,]

    # print the results
    if(printResults){
      mcmcSumDfPrim %>%
        kable(caption = paste(sep="","Summary of the MCMC parameter estimation (individual tests only) for country ",dfResPrimBayes$country[i],", region ",dfResPrimBayes$region[i],", setting ",dfResPrimBayes$setting[i]," (community-based screening set of diagnostic algorithms, W4SS entry).\nMetrics shown are the posterior mean, standard deviation, 2.5th, 50th, 97.5th quantiles, the Gelman-Rubin potential scale reduction factor (Rhat) and the effective sample size (n.eff).")) %>%
        kable_styling(full_width=FALSE)
    }
    
    # add the summary to the result object
    res[["mcmcSummary"]]<-mcmcSumDf
    
    allRes[[paste(sep="_",dfResPrimBayes$country[i],dfResPrimBayes$region[i],dfResPrimBayes$setting[i])]]<-res
    
    values<-numeric(0)
    tmpParList<-parsVectW4ss[!grepl(pattern="lrp|lrm",parsVectW4ss)]
    for(j in 1:length(tmpParList)){
      parTmp<-unlist(strsplit(split="\\.",tmpParList[j]))
      if(length(parTmp)==1){
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"))
      }else{
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"),paste(collapse=".",parTmp[2:length(parTmp)]))
      }
      if(!(tmpParList[j] %in% res$betaPars$par)){cat(paste(sep="","Parameter ",tmpParList[j]," not found in MCMC parameter list.\n"))}
      outTmp<-unlist(res$betaPars[res$betaPars$par==tmpParList[j],c("a","b")])
      names(outTmp)<-parTmp.ab
      values<-c(values,outTmp)
    }
    
  }else if(dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$entryToStudy[i]=="CXR" & dfResPrimBayes$country[i]!="Aggregate"){
    res<-analysisFunBayesBetaPars(dat=dfSim %>% filter(!is.na(cxr) & cxr==1),c=dfResPrimBayes$country[i],r=dfResPrimBayes$region[i],s=dfResPrimBayes$setting[i],doPlot=TRUE,plotFile=paste(sep="",outPrefix,"S4A_mcmcPars_",dfResPrimBayes$country[i],"_",dfResPrimBayes$region[i],"_",gsub(pattern=" / ",replacement="_",dfResPrimBayes$setting[i]),"_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile=jagsFileCxr,parsVect=parsVectCxrNoLR)
    
    # derive positive and negative likelihood ratios
    res$mcmcObj<-getLRpLRm(res$mcmcObj)
    
    # format the summary
    mcmcSumDf<-MCMCsummary(res$mcmcObj)
    mcmcSumDf$mean<-format(nsmall=3,round(digits=3,mcmcSumDf$mean))
    mcmcSumDf$sd<-format(nsmall=3,round(digits=3,mcmcSumDf$sd))
    mcmcSumDf$`2.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`2.5%`))
    mcmcSumDf$`50%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`50%`))
    mcmcSumDf$`97.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`97.5%`))
    mcmcSumDfPrim<-mcmcSumDf[parsVectPrim,]
    mcmcSumDf<-mcmcSumDf[parsVectCxr,]

    # print the results
    if(printResults){
      mcmcSumDfPrim %>%
        kable(caption = paste(sep="","Summary of the MCMC parameter estimation (individual tests only) for country ",dfResPrimBayes$country[i],", region ",dfResPrimBayes$region[i],", setting ",dfResPrimBayes$setting[i]," (community-based screening set of diagnostic algorithms, CXR-CAD entry).\nMetrics shown are the posterior mean, standard deviation, 2.5th, 50th, 97.5th quantiles, the Gelman-Rubin potential scale reduction factor (Rhat) and the effective sample size (n.eff).")) %>%
        kable_styling(full_width=FALSE)
    }
    
    # add the summary to the result object
    res[["mcmcSummary"]]<-mcmcSumDf
    
    allRes[[paste(sep="_",dfResPrimBayes$country[i],dfResPrimBayes$region[i],dfResPrimBayes$setting[i])]]<-res
    
    values<-numeric(0)
    tmpParList<-parsVectCxr[!grepl(pattern="lrp|lrm",parsVectCxr)]
    for(j in 1:length(tmpParList)){
      parTmp<-unlist(strsplit(split="\\.",tmpParList[j]))
      if(length(parTmp)==1){
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"))
      }else{
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"),paste(collapse=".",parTmp[2:length(parTmp)]))
      }
      if(!(tmpParList[j] %in% res$betaPars$par)){cat(paste(sep="","Parameter ",tmpParList[j]," not found in MCMC parameter list.\n"))}
      outTmp<-unlist(res$betaPars[res$betaPars$par==tmpParList[j],c("a","b")])
      names(outTmp)<-parTmp.ab
      values<-c(values,outTmp)
    }
    
  }else if(dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$entryToStudy[i]=="W4SS | CXR" & dfResPrimBayes$country[i]!="Aggregate"){
    res<-analysisFunBayesBetaPars(dat=dfSim %>% filter((!is.na(w4ss) & w4ss==1) | (!is.na(cxr) & cxr==1)),c=dfResPrimBayes$country[i],r=dfResPrimBayes$region[i],s=dfResPrimBayes$setting[i],doPlot=TRUE,plotFile=paste(sep="",outPrefix,"S4A_mcmcPars_",dfResPrimBayes$country[i],"_",dfResPrimBayes$region[i],"_",gsub(pattern=" / ",replacement="_",dfResPrimBayes$setting[i]),"_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile=jagsFileCxr,parsVect=parsVectCxrNoLR)
    
    # derive positive and negative likelihood ratios
    res$mcmcObj<-getLRpLRm(res$mcmcObj)
    
    # format the summary
    mcmcSumDf<-MCMCsummary(res$mcmcObj)
    mcmcSumDf$mean<-format(nsmall=3,round(digits=3,mcmcSumDf$mean))
    mcmcSumDf$sd<-format(nsmall=3,round(digits=3,mcmcSumDf$sd))
    mcmcSumDf$`2.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`2.5%`))
    mcmcSumDf$`50%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`50%`))
    mcmcSumDf$`97.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`97.5%`))
    mcmcSumDfPrim<-mcmcSumDf[parsVectPrim,]
    mcmcSumDf<-mcmcSumDf[parsVectCxr,]

    # print the results
    if(printResults){
      mcmcSumDfPrim %>%
        kable(caption = paste(sep="","Summary of the MCMC parameter estimation (individual tests only) for country ",dfResPrimBayes$country[i],", region ",dfResPrimBayes$region[i],", setting ",dfResPrimBayes$setting[i]," (community-based screening set of diagnostic algorithms, W4SS | CXR-CAD entry).\nMetrics shown are the posterior mean, standard deviation, 2.5th, 50th, 97.5th quantiles, the Gelman-Rubin potential scale reduction factor (Rhat) and the effective sample size (n.eff).")) %>%
        kable_styling(full_width=FALSE)
    }
    
    # add the summary to the result object
    res[["mcmcSummary"]]<-mcmcSumDf
    
    allRes[[paste(sep="_",dfResPrimBayes$country[i],dfResPrimBayes$region[i],dfResPrimBayes$setting[i])]]<-res
    
    values<-numeric(0)
    tmpParList<-parsVectCxr[!grepl(pattern="lrp|lrm",parsVectCxr)]
    for(j in 1:length(tmpParList)){
      parTmp<-unlist(strsplit(split="\\.",tmpParList[j]))
      if(length(parTmp)==1){
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"))
      }else{
        parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"),paste(collapse=".",parTmp[2:length(parTmp)]))
      }
      if(!(tmpParList[j] %in% res$betaPars$par)){cat(paste(sep="","Parameter ",tmpParList[j]," not found in MCMC parameter list.\n"))}
      outTmp<-unlist(res$betaPars[res$betaPars$par==tmpParList[j],c("a","b")])
      names(outTmp)<-parTmp.ab
      values<-c(values,outTmp)
    }
    
  }else if(dfResPrimBayes$setting[i]=="Children" & dfResPrimBayes$country[i]!="Aggregate"){
    cat("Children processing not yet implemented.\n")
    values<-NA
  }else if(dfResPrimBayes$country[i]!="Aggregate"){
    stop("Invalid setting parameter.")
  }
  
  if(!checkW4ssFacility & dfResPrimBayes$setting[i] %in% c("PHC","District") & dfResPrimBayes$country[i]!="Aggregate"){
    dfResW4ssFacility<-values
    checkW4ssFacility<-TRUE
  }else if(checkW4ssFacility & dfResPrimBayes$setting[i] %in% c("PHC","District") & dfResPrimBayes$country[i]!="Aggregate"){
    dfResW4ssFacility<-rbind(dfResW4ssFacility,values)
  }else if(!checkW4ssAcf & dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$country[i]!="Aggregate" & dfResPrimBayes$entryToStudy[i]=="W4SS"){
    dfResW4ssAcf<-values
    checkW4ssAcf<-TRUE
  }else if(checkW4ssAcf & dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$country[i]!="Aggregate" & dfResPrimBayes$entryToStudy[i]=="W4SS"){
    dfResW4ssAcf<-rbind(dfResW4ssAcf,values)
  }else if(!checkCxrAcf & dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$country[i]!="Aggregate" & dfResPrimBayes$entryToStudy[i]=="CXR"){
    dfResCxrAcf<-values
    checkCxrAcf<-TRUE
  }else if(checkCxrAcf & dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$country[i]!="Aggregate" & dfResPrimBayes$entryToStudy[i]=="CXR"){
    dfResCxrAcf<-rbind(dfResCxrAcf,values)
  }else if(!checkW4ssCxrAcf & dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$country[i]!="Aggregate" & dfResPrimBayes$entryToStudy[i]=="W4SS | CXR"){
    dfResW4ssCxrAcf<-values
    checkW4ssCxrAcf<-TRUE
  }else if(checkW4ssCxrAcf & dfResPrimBayes$setting[i] %in% c("IDP / refugees","Informal settlements","Nomads") & dfResPrimBayes$country[i]!="Aggregate" & dfResPrimBayes$entryToStudy[i]=="W4SS | CXR"){
    dfResW4ssCxrAcf<-rbind(dfResW4ssCxrAcf,values)
  }
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
Code
# Aggregate PHC
res<-analysisFunBayesBetaParsPool(dat=dfSim,set="PHC",doPlot=TRUE,plotFile=paste(sep="",outPrefix,"S4A_mcmcPars_Aggregate_PHC","_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile=jagsFileW4ssPooled,parsVect=parsVectW4ssNoLR)
NOTE: Stopping adaptation
Code
## derive positive and negative likelihood ratios
res$mcmcObj<-getLRpLRm(res$mcmcObj)

## format the summary
mcmcSumDf<-MCMCsummary(res$mcmcObj)
mcmcSumDf$mean<-format(nsmall=3,round(digits=3,mcmcSumDf$mean))
mcmcSumDf$sd<-format(nsmall=3,round(digits=3,mcmcSumDf$sd))
mcmcSumDf$`2.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`2.5%`))
mcmcSumDf$`50%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`50%`))
mcmcSumDf$`97.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`97.5%`))
mcmcSumDfPrim<-mcmcSumDf[parsVectPrim,]
mcmcSumDf<-mcmcSumDf[parsVectW4ss,]

# add the summary to the result object
res[["mcmcSummary"]]<-mcmcSumDf

allRes[["Aggregate__PHC"]]<-res

values<-numeric(0)
tmpParList<-parsVectW4ss[!grepl(pattern="lrp|lrm",parsVectW4ss)]
for(j in 1:length(tmpParList)){
  parTmp<-unlist(strsplit(split="\\.",tmpParList[j]))
  if(length(parTmp)==1){
    parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"))
  }else{
    parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"),paste(collapse=".",parTmp[2:length(parTmp)]))
  }
  if(!(tmpParList[j] %in% res$betaPars$par)){cat(paste(sep="","Parameter ",tmpParList[j]," not found in MCMC parameter list.\n"))}
  outTmp<-unlist(res$betaPars[res$betaPars$par==tmpParList[j],c("a","b")])
  names(outTmp)<-parTmp.ab
  values<-c(values,outTmp)
}

dfResW4ssFacility<-rbind(dfResW4ssFacility,values)

# Aggregate district
res<-analysisFunBayesBetaParsPool(dat=dfSim,set="District",doPlot=TRUE,plotFile=paste(sep="",outPrefix,"S4A_mcmcPars_Aggregate_District","_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),nChains=nChains,nAdapt=nAdapt,nBurn=nBurn,nIter=nIter,jagsFile=jagsFileW4ssPooled,parsVect=parsVectW4ssNoLR)
NOTE: Stopping adaptation
Code
## derive positive and negative likelihood ratios
res$mcmcObj<-getLRpLRm(res$mcmcObj)

## format the summary
mcmcSumDf<-MCMCsummary(res$mcmcObj)
mcmcSumDf$mean<-format(nsmall=3,round(digits=3,mcmcSumDf$mean))
mcmcSumDf$sd<-format(nsmall=3,round(digits=3,mcmcSumDf$sd))
mcmcSumDf$`2.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`2.5%`))
mcmcSumDf$`50%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`50%`))
mcmcSumDf$`97.5%`<-format(nsmall=3,round(digits=3,mcmcSumDf$`97.5%`))
mcmcSumDfPrim<-mcmcSumDf[parsVectPrim,]
mcmcSumDf<-mcmcSumDf[parsVectW4ss,]

# add the summary to the result object
res[["mcmcSummary"]]<-mcmcSumDf

allRes[["Aggregate__District"]]<-res

values<-numeric(0)
tmpParList<-parsVectW4ss[!grepl(pattern="lrp|lrm",parsVectW4ss)]
for(j in 1:length(tmpParList)){
  parTmp<-unlist(strsplit(split="\\.",tmpParList[j]))
  if(length(parTmp)==1){
    parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"))
  }else{
    parTmp.ab<-paste(sep=".",parTmp[1],c("a","b"),paste(collapse=".",parTmp[2:length(parTmp)]))
  }
  if(!(tmpParList[j] %in% res$betaPars$par)){cat(paste(sep="","Parameter ",tmpParList[j]," not found in MCMC parameter list.\n"))}
  outTmp<-unlist(res$betaPars[res$betaPars$par==tmpParList[j],c("a","b")])
  names(outTmp)<-parTmp.ab
  values<-c(values,outTmp)
}

dfResW4ssFacility<-rbind(dfResW4ssFacility,values)


if(!is.null(dim(dfResW4ssFacility))){
  dfResW4ssFacility<-cbind(dfResPrimBayes %>% dplyr::filter(setting %in% c("PHC","District")),dfResW4ssFacility)
  colnames(dfResW4ssFacility)<-c(colnames(dfResPrimBayes),parsVectW4ss)
}else{
  dfResW4ssFacility<-as.character(c(dfResPrimBayes %>% dplyr::filter(setting %in% c("PHC","District")),dfResW4ssFacility))
  names(dfResW4ssFacility)<-c(colnames(dfResPrimBayes),parsVectW4ss)
  dfResW4ssFacility<-t(as.data.frame(dfResW4ssFacility))
}

if(!is.null(dim(dfResW4ssAcf))){
  dfResW4ssAcf<-cbind(dfResPrimBayes %>% dplyr::filter(setting %in% c("IDP / refugees","Informal settlements","Nomads") & entryToStudy=="W4SS"),dfResW4ssAcf)
  colnames(dfResW4ssAcf)<-c(colnames(dfResPrimBayes),parsVectW4ss)
}else{
  dfResW4ssAcf<-as.character(c(dfResPrimBayes %>% dplyr::filter(setting %in% c("IDP / refugees","Informal settlements","Nomads") & entryToStudy=="W4SS"),dfResW4ssAcf))
  names(dfResW4ssAcf)<-c(colnames(dfResPrimBayes),parsVectW4ss)
  dfResW4ssAcf<-t(as.data.frame(dfResW4ssAcf))
}

if(!is.null(dim(dfResCxrAcf))){
  dfResCxrAcf<-cbind(dfResPrimBayes %>% dplyr::filter(setting %in% c("IDP / refugees","Informal settlements","Nomads") & entryToStudy=="CXR"),dfResCxrAcf)
  colnames(dfResCxrAcf)<-c(colnames(dfResPrimBayes),parsVectCxr)
}else{
  dfResCxrAcf<-as.character(c(dfResPrimBayes %>% dplyr::filter(setting %in% c("IDP / refugees","Informal settlements","Nomads") & entryToStudy=="CXR"),dfResCxrAcf))
  names(dfResCxrAcf)<-c(colnames(dfResPrimBayes),parsVectCxr)
  dfResCxrAcf<-t(as.data.frame(dfResCxrAcf))
}

if(!is.null(dim(dfResW4ssCxrAcf))){
  dfResW4ssCxrAcf<-cbind(dfResPrimBayes %>% dplyr::filter(setting %in% c("IDP / refugees","Informal settlements","Nomads") & entryToStudy=="W4SS | CXR"),dfResW4ssCxrAcf)
  colnames(dfResW4ssCxrAcf)<-c(colnames(dfResPrimBayes),parsVectCxr)
}else{
  dfResW4ssCxrAcf<-as.character(c(dfResPrimBayes %>% dplyr::filter(setting %in% c("IDP / refugees","Informal settlements","Nomads") & entryToStudy=="W4SS | CXR"),dfResW4ssCxrAcf))
  names(dfResW4ssCxrAcf)<-c(colnames(dfResPrimBayes),parsVectCxr)
  dfResW4ssCxrAcf<-t(as.data.frame(dfResW4ssCxrAcf))
}

write.csv(dfResW4ssFacility,row.names=F,file=paste(sep="","mockData_AnalysisResults_PCFICF_",gsub(pattern="-",replacement="",Sys.Date()),".csv"))
write.csv(dfResW4ssAcf,row.names=F,file=paste(sep="","mockData_AnalysisResults_ACF_W4SSentry",gsub(pattern="-",replacement="",Sys.Date()),".csv"))
write.csv(dfResCxrAcf,row.names=F,file=paste(sep="","mockData_AnalysisResults_ACF_CXRentry",gsub(pattern="-",replacement="",Sys.Date()),".csv"))
write.csv(dfResW4ssCxrAcf,row.names=F,file=paste(sep="","mockData_AnalysisResults_ACF_W4SSorCXRentry",gsub(pattern="-",replacement="",Sys.Date()),".csv"))
save(list=c("allRes","dfResPrimBayes"),file=paste(sep="","mockData_AnalysisResults_",gsub(pattern="-",replacement="",Sys.Date()),".RData"))

5.5.1.2 Facility-based lowest level of care

Below in Table 10 we present how we plan to present results for the overall facility-based evaluation at lowest levels of primary care.

Code
algoNames<-c("Poolxpert.Xpert",
             "Xpert",
             "Lam",
             "Cxr.Poolxpert.Xpert",
             "Crp.Poolxpert.Xpert",
             "Lam.Poolxpert.Xpert",
             "CxrCrpLam.Poolxpert.Xpert",
             "CxrLam.Poolxpert.Xpert",
             "CrpLam.Poolxpert.Xpert",
             "CxrCrp.Poolxpert.Xpert",
             "Cxr.Xpert",
             "Crp.Xpert",
             "Lam.Xpert",
             "CxrCrpLam.Xpert",
             "CxrLam.Xpert",
             "CrpLam.Xpert",
             "CxrCrp.Xpert",
             "Cxr.Lam",
             "Crp.Lam",
             "CxrCrp.Lam"
)

algoLabels<-c("Pooled Xpert Ultra --> Xpert Ultra",
              "Xpert Ultra",
              "LAM",
              "CXR-CAD --> Pooled Xpert Ultra --> Xpert Ultra",
              "CRP --> Pooled Xpert Ultra --> Xpert Ultra",
              "Lam --> Poolxpert --> Xpert",
              "CXR-CAD | CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
              "CXR-CAD | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
              "CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
              "CXR-CAD | CRP --> Pooled Xpert Ultra --> Xpert Ultra",
              "CXR-CAD --> Xpert Ultra",
              "CRP --> Xpert Ultra",
              "LAM --> Xpert Ultra",
              "CXR-CAD | CRP | LAM --> Xpert Ultra",
              "CXR-CAD | LAM --> Xpert Ultra",
              "CRP | LAM --> Xpert Ultra",
              "CXR-CAD | CRP --> Xpert Ultra",
              "CXR-CAD --> LAM",
              "CRP --> LAM",
              "CXR-CAD | CRP --> LAM"
)

settings<-paste(sep="_",dfResPrimBayes$country,dfResPrimBayes$region,dfResPrimBayes$setting)
settings<-settings[grepl(pattern="PHC",settings)]
settings<-settings[c(length(settings),1:(length(settings)-1))]

gr<-expand.grid(settings,algoNames)

dfAlgos<-data.frame(
  setting=gr[,1],
  algo=gr[,2],
  label=algoLabels[match(gr[,2],algoNames)],
  prev=NA,
  prevAS=NA,
  sens=NA,
  spec=NA,
  ppv=NA,
  npv=NA,
  dyt=NA,
  lrp=NA,
  lrm=NA
)

for(j in 1:nrow(dfAlgos)){
  res<-allRes[[as.character(dfAlgos$setting[j])]]
  par<-dfAlgos$algo[j]
  
  # prevalence
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary["ptb","50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary["ptb",c("2.5%","97.5%")])),")")
  dfAlgos$prev[j]<-paste(sep=" ",pointEstimate,ci)
  
  # prevalence of asymptomatics
  tmp<-unlist(strsplit(split="_",as.character(dfAlgos$setting[j])))
  if(tmp[1]!="Aggregate"){
    dfTmp<-dfSim %>% filter(country==tmp[1] & region==tmp[2] & setting==tmp[3])
  }else{
    dfTmp<-dfSim %>% filter(setting==tmp[3])
  }
  k<-sum(!dfTmp$w4ss)
  n<-nrow(dfTmp)
  pointEstimate<-round(digits=4,k/n)
  ci<-round(digits=4,binom.test(x=k,n=n)$conf.int)
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",ci)),")")
  dfAlgos$prevAS[j]<-paste(sep=" ",pointEstimate,ci)
  
  # sensitivity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","pse.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","pse.",par),c("2.5%","97.5%")])),")")
  dfAlgos$sens[j]<-paste(sep=" ",pointEstimate,ci)
  
  # specificity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","psp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","psp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$spec[j]<-paste(sep=" ",pointEstimate,ci)
  
  # PPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","ppv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","ppv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$ppv[j]<-paste(sep=" ",pointEstimate,ci)
  
  # NPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","npv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","npv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$npv[j]<-paste(sep=" ",pointEstimate,ci)
  
  # DYT
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","dyt.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","dyt.",par),c("2.5%","97.5%")])),")")
  dfAlgos$dyt[j]<-paste(sep=" ",pointEstimate,ci)
  
  # LR+
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrp[j]<-paste(sep=" ",pointEstimate,ci)
  
  # LR-
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrm.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrm.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrm[j]<-paste(sep=" ",pointEstimate,ci)
}

dfAlgos$setting<-gsub(pattern="_+",replacement=" ",dfAlgos$setting)

colnames(dfAlgos)<-case_when(
  colnames(dfAlgos)=="setting"~"Country & setting",
  colnames(dfAlgos)=="algo"~"Algorithm code",
  colnames(dfAlgos)=="label"~"Algorithm",
  colnames(dfAlgos)=="prev"~"Prevalence",
  colnames(dfAlgos)=="prevAS"~"Prevalence of asymptomatics",
  colnames(dfAlgos)=="sens"~"Sensitivity",
  colnames(dfAlgos)=="spec"~"Specificity",
  colnames(dfAlgos)=="ppv"~"PPV",
  colnames(dfAlgos)=="npv"~"NPV",
  colnames(dfAlgos)=="dyt"~"DYT",
  colnames(dfAlgos)=="dyd"~"DYD",
  colnames(dfAlgos)=="lrp"~"Positive LR",
  colnames(dfAlgos)=="lrm"~"Negative LR"
)

dfAlgos %>%
  dplyr::select(!contains("code") & !contains("DYD") & !contains("Algorithm")) %>%
  kable(caption="Facility-based algorithm evaluation for PHC / lowest levels of primary care settings (all participants). Asymptomatics proportions are estimated directly, using exact binomial confidence intervals, from the data (not modelled). All other parameters are posterior estimates from the Bayesian modelling approach.") %>%
  kableExtra::kable_styling(full_width = FALSE) %>%
  kableExtra::group_rows(index=table(fct_inorder(dfAlgos$Algorithm)))
Table 10: Facility-based evaluation (all participants): Bayesian posterior estimates for sensitivity, specificity, positive and negative predictive values and positive and negative likelihood ratios for PHC / lowest level of primary care settings.
Facility-based algorithm evaluation for PHC / lowest levels of primary care settings (all participants). Asymptomatics proportions are estimated directly, using exact binomial confidence intervals, from the data (not modelled). All other parameters are posterior estimates from the Bayesian modelling approach.
Country & setting Prevalence Prevalence of asymptomatics Sensitivity Specificity PPV NPV DYT Positive LR Negative LR
Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.907 (0.883,0.928) 1.000 (0.999,1.000) 0.996 (0.989,0.999) 0.988 (0.985,0.991) 0.103 (0.095,0.111) 2244.184 (726.333,13102.451) 0.093 (0.072,0.117)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.937 (0.869,0.977) 0.999 (0.995,1.000) 0.987 (0.944,0.999) 0.995 (0.988,0.998) 0.075 (0.060,0.092) 845.542 (197.541,10960.422) 0.063 (0.023,0.132)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.902 (0.848,0.942) 1.000 (0.996,1.000) 0.999 (0.986,1.000) 0.973 (0.958,0.985) 0.197 (0.169,0.228) 4952.075 (244.951,92842.485) 0.098 (0.058,0.152)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.916 (0.827,0.968) 0.998 (0.993,1.000) 0.982 (0.924,0.999) 0.992 (0.984,0.997) 0.079 (0.060,0.101) 573.928 (136.849,7306.975) 0.085 (0.032,0.174)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.853 (0.713,0.943) 1.000 (0.998,1.000) 0.997 (0.934,1.000) 0.995 (0.989,0.998) 0.029 (0.020,0.041) 8345.418 (396.628,91988.466) 0.147 (0.057,0.287)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.906 (0.825,0.959) 1.000 (0.995,1.000) 0.999 (0.970,1.000) 0.984 (0.969,0.993) 0.134 (0.106,0.166) 4181.264 (198.373,94144.304) 0.094 (0.041,0.175)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.915 (0.863,0.953) 1.000 (0.995,1.000) 0.999 (0.985,1.000) 0.968 (0.947,0.983) 0.258 (0.221,0.298) 3158.693 (167.291,93688.563) 0.085 (0.047,0.137)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.892 (0.818,0.943) 1.000 (0.998,1.000) 0.999 (0.977,1.000) 0.991 (0.984,0.995) 0.069 (0.056,0.084) 9889.839 (466.274,92826.934) 0.108 (0.057,0.182)
Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.983 (0.968,0.992) 0.991 (0.987,0.993) 0.931 (0.908,0.951) 0.998 (0.996,0.999) 0.119 (0.111,0.128) 105.256 (77.705,149.494) 0.017 (0.008,0.032)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.990 (0.949,1.000) 0.995 (0.988,0.998) 0.944 (0.868,0.980) 0.999 (0.996,1.000) 0.083 (0.067,0.101) 195.232 (79.123,563.398) 0.010 (0.000,0.051)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.966 (0.927,0.986) 0.987 (0.975,0.995) 0.953 (0.913,0.981) 0.990 (0.979,0.996) 0.222 (0.193,0.252) 71.818 (38.734,183.271) 0.034 (0.014,0.074)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.996 (0.947,0.999) 0.987 (0.975,0.993) 0.874 (0.777,0.929) 1.000 (0.995,1.000) 0.096 (0.077,0.119) 75.968 (40.282,135.441) 0.004 (0.001,0.054)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.993 (0.904,1.000) 0.988 (0.980,0.993) 0.736 (0.604,0.844) 1.000 (0.996,1.000) 0.046 (0.035,0.059) 79.049 (50.331,134.351) 0.007 (0.000,0.097)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.973 (0.925,0.997) 0.997 (0.990,0.999) 0.983 (0.947,0.994) 0.995 (0.987,0.999) 0.147 (0.118,0.179) 320.710 (100.821,1004.887) 0.027 (0.003,0.076)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.987 (0.966,0.998) 0.995 (0.987,0.999) 0.987 (0.967,0.997) 0.995 (0.986,0.999) 0.283 (0.247,0.318) 189.409 (74.482,743.705) 0.013 (0.002,0.035)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.986 (0.959,0.997) 0.989 (0.982,0.995) 0.882 (0.809,0.941) 0.999 (0.996,1.000) 0.087 (0.071,0.103) 89.114 (53.785,180.251) 0.014 (0.003,0.042)
LAM
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.813 (0.777,0.843) 0.898 (0.888,0.907) 0.503 (0.472,0.535) 0.974 (0.969,0.979) 0.182 (0.172,0.193) 7.952 (7.230,8.781) 0.209 (0.174,0.249)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.831 (0.749,0.904) 0.890 (0.869,0.910) 0.394 (0.323,0.469) 0.984 (0.975,0.991) 0.167 (0.144,0.191) 7.597 (6.200,9.410) 0.189 (0.109,0.281)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.814 (0.746,0.870) 0.887 (0.858,0.912) 0.669 (0.599,0.731) 0.945 (0.923,0.961) 0.266 (0.236,0.299) 7.185 (5.605,9.210) 0.209 (0.148,0.290)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.813 (0.714,0.894) 0.879 (0.852,0.904) 0.383 (0.301,0.466) 0.981 (0.969,0.989) 0.180 (0.152,0.208) 6.721 (5.206,8.414) 0.213 (0.122,0.326)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.809 (0.638,0.930) 0.917 (0.899,0.933) 0.254 (0.178,0.341) 0.993 (0.984,0.997) 0.108 (0.090,0.128) 9.675 (7.013,12.470) 0.208 (0.076,0.396)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.836 (0.742,0.912) 0.911 (0.881,0.937) 0.620 (0.521,0.713) 0.970 (0.949,0.984) 0.199 (0.166,0.235) 9.405 (6.798,13.398) 0.180 (0.096,0.286)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.773 (0.703,0.832) 0.885 (0.850,0.918) 0.725 (0.657,0.794) 0.909 (0.877,0.935) 0.301 (0.261,0.341) 6.698 (5.199,9.420) 0.257 (0.188,0.335)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.816 (0.722,0.892) 0.919 (0.903,0.933) 0.458 (0.382,0.532) 0.984 (0.974,0.990) 0.138 (0.120,0.158) 10.072 (8.132,12.510) 0.200 (0.117,0.302)
CXR-CAD --> Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.796 (0.764,0.827) 1.000 (1.000,1.000) 1.000 (0.996,1.000) 0.975 (0.970,0.979) 0.090 (0.083,0.098) 38356.857 (1883.377,81996.143) 0.204 (0.173,0.236)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.798 (0.702,0.875) 1.000 (0.998,1.000) 0.998 (0.967,1.000) 0.983 (0.973,0.990) 0.063 (0.049,0.079) 7774.610 (354.890,85118.852) 0.202 (0.125,0.298)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.797 (0.731,0.856) 1.000 (0.996,1.000) 0.999 (0.984,1.000) 0.946 (0.927,0.962) 0.174 (0.147,0.204) 4670.139 (206.244,83526.421) 0.203 (0.144,0.269)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.713 (0.591,0.819) 1.000 (0.997,1.000) 0.998 (0.952,1.000) 0.974 (0.960,0.984) 0.060 (0.044,0.079) 4901.686 (233.203,77772.966) 0.288 (0.181,0.409)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.825 (0.677,0.927) 1.000 (0.998,1.000) 0.997 (0.931,1.000) 0.994 (0.987,0.998) 0.028 (0.019,0.039) 8261.076 (398.692,89536.754) 0.176 (0.074,0.323)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.851 (0.759,0.921) 1.000 (0.995,1.000) 0.999 (0.969,1.000) 0.975 (0.957,0.987) 0.126 (0.099,0.158) 3814.190 (183.971,89139.457) 0.149 (0.079,0.241)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.787 (0.717,0.849) 1.000 (0.995,1.000) 0.999 (0.982,1.000) 0.923 (0.895,0.947) 0.222 (0.188,0.260) 3014.665 (144.116,82261.673) 0.213 (0.152,0.283)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.806 (0.718,0.877) 1.000 (0.998,1.000) 0.999 (0.975,1.000) 0.984 (0.975,0.990) 0.063 (0.050,0.078) 9319.688 (420.599,85423.997) 0.194 (0.123,0.282)
CRP --> Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.807 (0.775,0.837) 1.000 (0.999,1.000) 0.996 (0.988,0.999) 0.976 (0.972,0.980) 0.092 (0.084,0.100) 1964.475 (663.425,10792.354) 0.193 (0.163,0.225)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.785 (0.688,0.864) 0.999 (0.995,1.000) 0.984 (0.931,0.999) 0.982 (0.972,0.989) 0.063 (0.049,0.079) 695.583 (162.554,10127.031) 0.215 (0.136,0.312)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.863 (0.801,0.911) 1.000 (0.996,1.000) 0.999 (0.985,1.000) 0.963 (0.944,0.976) 0.188 (0.161,0.219) 4858.850 (234.398,89203.425) 0.137 (0.089,0.199)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.813 (0.702,0.899) 0.998 (0.993,1.000) 0.979 (0.913,0.998) 0.983 (0.971,0.991) 0.070 (0.053,0.091) 508.120 (120.129,6744.898) 0.187 (0.101,0.299)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.736 (0.573,0.861) 1.000 (0.998,1.000) 0.996 (0.924,1.000) 0.991 (0.983,0.996) 0.025 (0.017,0.036) 6980.143 (352.320,82237.150) 0.265 (0.139,0.427)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.769 (0.668,0.858) 1.000 (0.995,1.000) 0.998 (0.966,1.000) 0.962 (0.942,0.977) 0.114 (0.088,0.144) 3455.778 (160.320,81742.868) 0.231 (0.142,0.333)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.787 (0.715,0.848) 1.000 (0.995,1.000) 0.999 (0.981,1.000) 0.923 (0.893,0.947) 0.222 (0.187,0.260) 2934.396 (145.557,82017.203) 0.213 (0.152,0.285)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.817 (0.730,0.886) 1.000 (0.998,1.000) 0.999 (0.975,1.000) 0.985 (0.977,0.991) 0.063 (0.050,0.078) 9526.868 (452.519,86511.356) 0.183 (0.114,0.271)
Lam --> Poolxpert --> Xpert
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.739 (0.704,0.773) 1.000 (1.000,1.000) 1.000 (0.996,1.000) 0.968 (0.963,0.972) 0.084 (0.076,0.091) 41275.389 (1820.456,76636.133) 0.261 (0.227,0.296)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.772 (0.672,0.854) 1.000 (0.998,1.000) 0.999 (0.967,1.000) 0.981 (0.971,0.988) 0.061 (0.047,0.077) 7424.145 (343.636,82895.563) 0.228 (0.146,0.328)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.719 (0.646,0.787) 1.000 (0.996,1.000) 0.999 (0.982,1.000) 0.927 (0.905,0.946) 0.157 (0.132,0.185) 4107.619 (195.043,75906.969) 0.281 (0.213,0.354)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.764 (0.647,0.859) 1.000 (0.997,1.000) 0.998 (0.956,1.000) 0.979 (0.966,0.988) 0.064 (0.048,0.084) 5273.532 (243.685,82322.050) 0.236 (0.141,0.354)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.735 (0.572,0.866) 1.000 (0.998,1.000) 0.996 (0.923,1.000) 0.991 (0.983,0.996) 0.025 (0.017,0.036) 7534.236 (352.131,82489.880) 0.265 (0.134,0.428)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.784 (0.682,0.868) 1.000 (0.995,1.000) 0.998 (0.966,1.000) 0.964 (0.943,0.979) 0.116 (0.090,0.146) 3357.732 (165.243,83205.104) 0.217 (0.132,0.319)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.717 (0.639,0.786) 1.000 (0.994,1.000) 0.999 (0.979,1.000) 0.900 (0.867,0.926) 0.202 (0.169,0.239) 2580.561 (126.553,75625.273) 0.283 (0.214,0.362)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.731 (0.638,0.811) 1.000 (0.998,1.000) 0.999 (0.972,1.000) 0.978 (0.968,0.985) 0.057 (0.044,0.071) 8701.691 (418.145,78672.912) 0.269 (0.189,0.362)
CXR-CAD | CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.902 (0.877,0.924) 1.000 (0.999,1.000) 0.996 (0.989,0.999) 0.988 (0.984,0.990) 0.102 (0.095,0.110) 2242.199 (746.517,12406.321) 0.098 (0.076,0.123)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.924 (0.854,0.968) 0.999 (0.995,1.000) 0.986 (0.942,0.999) 0.994 (0.987,0.997) 0.074 (0.059,0.091) 828.852 (201.649,11661.553) 0.076 (0.032,0.146)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.902 (0.848,0.943) 1.000 (0.996,1.000) 0.999 (0.986,1.000) 0.973 (0.958,0.985) 0.197 (0.170,0.228) 5166.516 (243.861,92710.530) 0.098 (0.057,0.152)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.915 (0.829,0.968) 0.998 (0.993,1.000) 0.982 (0.922,0.999) 0.992 (0.984,0.997) 0.079 (0.060,0.100) 576.467 (134.543,8143.955) 0.085 (0.033,0.172)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.825 (0.675,0.926) 1.000 (0.998,1.000) 0.997 (0.933,1.000) 0.994 (0.987,0.998) 0.028 (0.019,0.040) 8798.516 (380.757,89871.971) 0.175 (0.074,0.325)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.905 (0.827,0.958) 1.000 (0.995,1.000) 0.999 (0.970,1.000) 0.984 (0.969,0.993) 0.134 (0.106,0.165) 3934.357 (190.263,93669.067) 0.095 (0.042,0.174)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.908 (0.853,0.948) 1.000 (0.994,1.000) 0.999 (0.984,1.000) 0.965 (0.942,0.981) 0.256 (0.219,0.296) 3544.165 (162.929,93091.122) 0.092 (0.052,0.147)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.892 (0.818,0.944) 1.000 (0.998,1.000) 0.999 (0.976,1.000) 0.991 (0.984,0.996) 0.069 (0.056,0.084) 10618.328 (505.802,92833.992) 0.108 (0.056,0.182)
CXR-CAD | LAM --> Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.883 (0.857,0.906) 1.000 (1.000,1.000) 1.000 (0.996,1.000) 0.985 (0.982,0.988) 0.100 (0.092,0.108) 48522.555 (2221.998,90139.789) 0.117 (0.094,0.143)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.899 (0.821,0.952) 1.000 (0.998,1.000) 0.999 (0.971,1.000) 0.991 (0.984,0.996) 0.071 (0.056,0.088) 8722.103 (412.756,93604.356) 0.101 (0.048,0.179)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.883 (0.824,0.927) 1.000 (0.996,1.000) 0.999 (0.985,1.000) 0.968 (0.951,0.980) 0.193 (0.165,0.223) 5131.494 (236.263,90937.039) 0.118 (0.073,0.176)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.898 (0.804,0.957) 1.000 (0.997,1.000) 0.998 (0.963,1.000) 0.991 (0.981,0.996) 0.076 (0.058,0.097) 5714.565 (280.167,93641.491) 0.102 (0.043,0.196)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.822 (0.676,0.926) 1.000 (0.998,1.000) 0.997 (0.930,1.000) 0.994 (0.988,0.997) 0.028 (0.019,0.039) 8469.825 (393.815,89641.358) 0.178 (0.075,0.324)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.906 (0.824,0.957) 1.000 (0.995,1.000) 0.999 (0.972,1.000) 0.984 (0.969,0.993) 0.134 (0.106,0.166) 4086.377 (193.555,93643.522) 0.094 (0.043,0.176)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.886 (0.827,0.932) 1.000 (0.995,1.000) 0.999 (0.984,1.000) 0.957 (0.934,0.975) 0.250 (0.213,0.289) 3202.629 (159.448,91214.588) 0.114 (0.068,0.173)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.861 (0.779,0.921) 1.000 (0.998,1.000) 0.999 (0.975,1.000) 0.988 (0.981,0.993) 0.067 (0.054,0.082) 9840.791 (461.907,90327.323) 0.139 (0.079,0.221)
CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.891 (0.865,0.914) 1.000 (0.999,1.000) 0.996 (0.989,0.999) 0.986 (0.983,0.989) 0.101 (0.094,0.109) 2222.346 (717.725,12191.099) 0.109 (0.086,0.135)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.899 (0.819,0.952) 0.999 (0.995,1.000) 0.986 (0.941,0.999) 0.991 (0.984,0.996) 0.072 (0.057,0.089) 814.989 (187.670,11650.397) 0.101 (0.048,0.181)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.895 (0.840,0.937) 1.000 (0.996,1.000) 0.999 (0.986,1.000) 0.972 (0.956,0.983) 0.196 (0.168,0.226) 4858.446 (246.919,92098.227) 0.105 (0.063,0.160)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.915 (0.827,0.967) 0.998 (0.993,1.000) 0.981 (0.923,0.999) 0.992 (0.983,0.997) 0.078 (0.060,0.100) 570.151 (132.022,8361.802) 0.085 (0.033,0.173)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.794 (0.637,0.906) 1.000 (0.998,1.000) 0.997 (0.925,1.000) 0.993 (0.986,0.997) 0.027 (0.018,0.038) 8134.106 (371.987,87202.476) 0.206 (0.094,0.363)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.906 (0.826,0.958) 1.000 (0.996,1.000) 0.999 (0.970,1.000) 0.984 (0.969,0.993) 0.134 (0.106,0.166) 3973.034 (207.072,93838.503) 0.094 (0.042,0.174)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.893 (0.835,0.937) 1.000 (0.995,1.000) 0.999 (0.984,1.000) 0.960 (0.936,0.977) 0.252 (0.215,0.292) 3325.439 (165.999,91946.551) 0.107 (0.063,0.165)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.882 (0.805,0.936) 1.000 (0.998,1.000) 0.999 (0.975,1.000) 0.990 (0.983,0.995) 0.068 (0.055,0.084) 10095.705 (489.902,91966.459) 0.118 (0.064,0.195)
CXR-CAD | CRP --> Pooled Xpert Ultra --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.885 (0.858,0.908) 1.000 (0.999,1.000) 0.996 (0.989,0.999) 0.986 (0.982,0.989) 0.100 (0.093,0.108) 2195.906 (713.382,12073.733) 0.115 (0.092,0.142)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.886 (0.806,0.944) 0.999 (0.995,1.000) 0.986 (0.940,0.999) 0.990 (0.983,0.995) 0.071 (0.056,0.088) 802.505 (188.402,12165.246) 0.114 (0.056,0.195)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.902 (0.848,0.942) 1.000 (0.996,1.000) 0.999 (0.986,1.000) 0.973 (0.958,0.984) 0.197 (0.169,0.228) 5071.559 (240.767,92637.651) 0.098 (0.058,0.152)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.882 (0.786,0.948) 0.998 (0.993,1.000) 0.981 (0.922,0.999) 0.989 (0.979,0.995) 0.075 (0.058,0.097) 552.030 (123.271,7850.354) 0.119 (0.052,0.215)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.825 (0.683,0.924) 1.000 (0.998,1.000) 0.997 (0.928,1.000) 0.994 (0.988,0.997) 0.028 (0.019,0.039) 8375.388 (389.459,89214.383) 0.176 (0.076,0.317)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.892 (0.808,0.949) 1.000 (0.995,1.000) 0.999 (0.971,1.000) 0.982 (0.966,0.991) 0.132 (0.104,0.164) 3982.124 (192.220,92622.779) 0.108 (0.051,0.192)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.887 (0.827,0.931) 1.000 (0.995,1.000) 0.999 (0.984,1.000) 0.957 (0.934,0.975) 0.250 (0.213,0.289) 3335.886 (164.566,91359.876) 0.113 (0.069,0.174)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.871 (0.794,0.929) 1.000 (0.998,1.000) 0.999 (0.977,1.000) 0.989 (0.982,0.994) 0.067 (0.054,0.083) 9882.431 (465.280,91285.376) 0.129 (0.071,0.206)
CXR-CAD --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.867 (0.840,0.892) 0.999 (0.998,1.000) 0.991 (0.981,0.997) 0.983 (0.980,0.987) 0.099 (0.091,0.107) 868.457 (402.330,2476.067) 0.133 (0.109,0.161)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.847 (0.758,0.916) 0.999 (0.996,1.000) 0.985 (0.935,0.999) 0.987 (0.979,0.993) 0.068 (0.053,0.084) 766.039 (183.966,10099.435) 0.153 (0.084,0.242)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.863 (0.803,0.911) 0.998 (0.992,1.000) 0.992 (0.968,0.999) 0.963 (0.946,0.976) 0.190 (0.162,0.220) 463.250 (107.949,5798.118) 0.138 (0.089,0.197)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.796 (0.681,0.886) 1.000 (0.997,1.000) 0.998 (0.959,1.000) 0.981 (0.969,0.990) 0.067 (0.050,0.087) 5300.726 (251.362,85445.460) 0.204 (0.114,0.319)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.970 (0.877,0.998) 0.999 (0.995,1.000) 0.971 (0.876,0.998) 0.999 (0.995,1.000) 0.034 (0.024,0.047) 922.103 (212.027,12928.604) 0.030 (0.002,0.123)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.906 (0.825,0.958) 1.000 (0.995,1.000) 0.999 (0.969,1.000) 0.984 (0.969,0.993) 0.134 (0.106,0.165) 3771.517 (183.753,93810.514) 0.094 (0.042,0.175)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.852 (0.786,0.903) 1.000 (0.994,1.000) 0.999 (0.984,1.000) 0.944 (0.919,0.964) 0.240 (0.204,0.278) 3077.744 (147.081,87998.879) 0.148 (0.097,0.214)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.893 (0.821,0.945) 0.998 (0.995,1.000) 0.976 (0.929,0.996) 0.991 (0.984,0.995) 0.071 (0.057,0.086) 492.040 (161.397,2694.576) 0.107 (0.055,0.180)
CRP --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.880 (0.852,0.903) 0.999 (0.998,1.000) 0.991 (0.981,0.997) 0.985 (0.981,0.988) 0.100 (0.093,0.108) 870.795 (412.912,2337.403) 0.120 (0.097,0.148)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.835 (0.745,0.904) 0.999 (0.995,1.000) 0.985 (0.937,0.999) 0.986 (0.977,0.992) 0.067 (0.053,0.084) 751.303 (172.916,11070.434) 0.165 (0.096,0.256)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.928 (0.880,0.962) 0.998 (0.992,1.000) 0.993 (0.970,1.000) 0.980 (0.967,0.990) 0.204 (0.176,0.235) 498.018 (117.063,6550.475) 0.072 (0.038,0.120)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.898 (0.806,0.958) 0.997 (0.990,0.999) 0.964 (0.892,0.993) 0.991 (0.981,0.996) 0.079 (0.060,0.100) 288.186 (93.244,1697.416) 0.102 (0.042,0.195)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.882 (0.750,0.960) 1.000 (0.998,1.000) 0.997 (0.936,1.000) 0.996 (0.990,0.999) 0.030 (0.021,0.042) 8749.800 (403.931,94049.911) 0.118 (0.040,0.250)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.824 (0.730,0.900) 1.000 (0.996,1.000) 0.998 (0.965,1.000) 0.970 (0.952,0.983) 0.122 (0.095,0.152) 3618.810 (186.503,87105.680) 0.176 (0.100,0.270)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.859 (0.796,0.909) 0.997 (0.988,1.000) 0.992 (0.965,0.999) 0.947 (0.921,0.967) 0.244 (0.207,0.283) 303.199 (71.788,4601.784) 0.142 (0.091,0.206)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.903 (0.832,0.952) 1.000 (0.998,1.000) 0.999 (0.977,1.000) 0.992 (0.986,0.996) 0.070 (0.056,0.085) 10000.960 (495.914,93705.933) 0.097 (0.048,0.168)
LAM --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.795 (0.762,0.824) 0.999 (0.998,1.000) 0.988 (0.976,0.995) 0.974 (0.970,0.979) 0.091 (0.084,0.099) 655.111 (328.445,1666.409) 0.206 (0.176,0.239)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.811 (0.714,0.887) 0.999 (0.995,1.000) 0.984 (0.936,0.999) 0.984 (0.974,0.991) 0.065 (0.051,0.082) 745.361 (172.115,10004.897) 0.189 (0.113,0.286)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.778 (0.710,0.840) 0.998 (0.992,1.000) 0.991 (0.965,0.999) 0.941 (0.921,0.959) 0.171 (0.145,0.201) 420.799 (96.241,6208.171) 0.223 (0.160,0.291)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.813 (0.699,0.898) 0.997 (0.990,0.999) 0.959 (0.881,0.993) 0.983 (0.971,0.991) 0.071 (0.054,0.091) 258.530 (84.558,1418.595) 0.187 (0.102,0.302)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.826 (0.672,0.927) 1.000 (0.998,1.000) 0.997 (0.931,1.000) 0.994 (0.988,0.997) 0.028 (0.019,0.040) 8142.792 (380.619,89838.668) 0.174 (0.073,0.328)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.823 (0.727,0.898) 1.000 (0.995,1.000) 0.998 (0.968,1.000) 0.970 (0.952,0.983) 0.122 (0.096,0.153) 3719.076 (178.694,86762.618) 0.177 (0.102,0.273)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.766 (0.691,0.830) 1.000 (0.994,1.000) 0.999 (0.982,1.000) 0.916 (0.885,0.941) 0.216 (0.182,0.253) 2683.652 (134.872,80245.656) 0.234 (0.170,0.309)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.806 (0.720,0.876) 0.998 (0.994,1.000) 0.974 (0.923,0.996) 0.984 (0.976,0.990) 0.064 (0.051,0.079) 446.764 (143.172,2356.419) 0.195 (0.124,0.280)
CXR-CAD | CRP | LAM --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.979 (0.966,0.988) 0.997 (0.996,0.999) 0.980 (0.966,0.989) 0.997 (0.996,0.999) 0.113 (0.105,0.121) 374.505 (226.908,683.874) 0.021 (0.012,0.034)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.975 (0.923,0.996) 0.998 (0.993,1.000) 0.974 (0.925,0.995) 0.998 (0.993,1.000) 0.079 (0.064,0.097) 450.971 (145.104,2444.248) 0.025 (0.004,0.078)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.967 (0.932,0.988) 0.995 (0.986,0.999) 0.980 (0.950,0.995) 0.991 (0.980,0.997) 0.216 (0.186,0.247) 175.755 (67.151,693.435) 0.033 (0.012,0.069)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.998 (0.966,1.000) 0.995 (0.988,0.999) 0.952 (0.878,0.987) 1.000 (0.997,1.000) 0.089 (0.069,0.111) 213.704 (81.890,863.236) 0.002 (0.000,0.034)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.970 (0.878,0.998) 0.999 (0.995,1.000) 0.970 (0.876,0.998) 0.999 (0.995,1.000) 0.034 (0.024,0.047) 888.801 (209.894,12853.193) 0.030 (0.002,0.122)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.972 (0.920,0.995) 1.000 (0.995,1.000) 0.999 (0.972,1.000) 0.995 (0.986,0.999) 0.144 (0.116,0.176) 4287.051 (190.493,98762.246) 0.028 (0.005,0.080)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.986 (0.958,0.997) 0.997 (0.988,1.000) 0.993 (0.969,0.999) 0.994 (0.983,0.999) 0.281 (0.242,0.321) 342.166 (82.734,5116.206) 0.014 (0.003,0.043)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.989 (0.954,0.999) 0.997 (0.993,0.999) 0.969 (0.921,0.992) 0.999 (0.996,1.000) 0.079 (0.065,0.095) 365.914 (142.759,1493.465) 0.011 (0.001,0.046)
CXR-CAD | LAM --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.957 (0.940,0.971) 0.998 (0.997,0.999) 0.985 (0.974,0.993) 0.995 (0.992,0.996) 0.110 (0.102,0.118) 525.441 (293.802,1096.959) 0.043 (0.029,0.060)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.949 (0.889,0.984) 0.999 (0.995,1.000) 0.987 (0.944,0.999) 0.996 (0.990,0.999) 0.076 (0.061,0.094) 879.266 (203.140,11772.325) 0.051 (0.017,0.112)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.948 (0.903,0.975) 0.996 (0.989,0.999) 0.986 (0.959,0.998) 0.985 (0.973,0.993) 0.210 (0.181,0.241) 259.323 (84.670,1507.012) 0.053 (0.025,0.098)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.983 (0.927,0.999) 0.997 (0.991,0.999) 0.967 (0.902,0.994) 0.998 (0.993,1.000) 0.086 (0.066,0.107) 312.463 (104.611,1843.791) 0.017 (0.001,0.073)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.969 (0.878,0.998) 0.999 (0.996,1.000) 0.970 (0.879,0.998) 0.999 (0.996,1.000) 0.034 (0.024,0.047) 912.327 (214.025,12166.436) 0.031 (0.002,0.122)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.973 (0.920,0.995) 1.000 (0.995,1.000) 0.999 (0.972,1.000) 0.995 (0.986,0.999) 0.144 (0.115,0.177) 4064.602 (203.356,98825.193) 0.027 (0.005,0.080)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.958 (0.917,0.983) 1.000 (0.994,1.000) 0.999 (0.985,1.000) 0.984 (0.967,0.993) 0.270 (0.232,0.311) 3531.902 (159.080,97291.791) 0.042 (0.017,0.084)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.947 (0.888,0.980) 0.997 (0.993,0.999) 0.967 (0.914,0.991) 0.995 (0.990,0.998) 0.076 (0.062,0.092) 350.030 (138.452,1381.261) 0.053 (0.020,0.113)
CRP | LAM --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.968 (0.953,0.980) 0.998 (0.996,0.999) 0.984 (0.972,0.992) 0.996 (0.994,0.997) 0.111 (0.103,0.120) 482.629 (273.933,965.196) 0.032 (0.020,0.047)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.949 (0.886,0.984) 0.998 (0.993,1.000) 0.974 (0.921,0.995) 0.996 (0.990,0.999) 0.077 (0.061,0.094) 442.067 (140.652,2494.568) 0.051 (0.016,0.114)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.961 (0.921,0.984) 0.996 (0.989,0.999) 0.987 (0.959,0.998) 0.989 (0.978,0.996) 0.213 (0.183,0.244) 260.033 (84.757,1471.906) 0.039 (0.016,0.079)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.998 (0.966,1.000) 0.995 (0.988,0.999) 0.951 (0.879,0.988) 1.000 (0.997,1.000) 0.088 (0.069,0.111) 211.821 (83.094,848.620) 0.002 (0.000,0.034)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.940 (0.826,0.990) 1.000 (0.998,1.000) 0.997 (0.939,1.000) 0.998 (0.994,1.000) 0.032 (0.022,0.044) 10189.802 (430.838,97817.578) 0.060 (0.010,0.174)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.973 (0.922,0.995) 1.000 (0.995,1.000) 0.999 (0.973,1.000) 0.995 (0.985,0.999) 0.144 (0.115,0.176) 4252.535 (212.257,98844.628) 0.027 (0.005,0.078)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.971 (0.935,0.991) 0.997 (0.988,1.000) 0.993 (0.969,0.999) 0.989 (0.975,0.996) 0.276 (0.237,0.316) 343.559 (80.247,4888.353) 0.029 (0.009,0.065)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.978 (0.936,0.996) 0.998 (0.994,1.000) 0.978 (0.936,0.996) 0.998 (0.995,1.000) 0.078 (0.063,0.094) 534.573 (175.180,2929.297) 0.022 (0.004,0.064)
CXR-CAD | CRP --> Xpert Ultra
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.962 (0.946,0.975) 0.998 (0.996,0.999) 0.984 (0.972,0.992) 0.995 (0.993,0.997) 0.111 (0.102,0.119) 475.945 (271.930,945.452) 0.038 (0.025,0.054)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.936 (0.868,0.977) 0.998 (0.993,1.000) 0.974 (0.923,0.995) 0.995 (0.988,0.998) 0.076 (0.060,0.094) 425.113 (142.252,2316.636) 0.064 (0.023,0.133)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.967 (0.930,0.988) 0.996 (0.989,0.999) 0.987 (0.959,0.998) 0.991 (0.981,0.997) 0.214 (0.185,0.246) 259.014 (85.521,1509.907) 0.033 (0.012,0.070)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.966 (0.901,0.994) 0.997 (0.990,0.999) 0.966 (0.901,0.994) 0.997 (0.991,0.999) 0.084 (0.065,0.107) 306.962 (99.146,1723.372) 0.034 (0.006,0.099)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.970 (0.877,0.998) 0.999 (0.996,1.000) 0.971 (0.879,0.998) 0.999 (0.995,1.000) 0.034 (0.024,0.046) 887.890 (214.018,11782.005) 0.030 (0.002,0.124)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.959 (0.897,0.989) 1.000 (0.995,1.000) 0.999 (0.970,1.000) 0.993 (0.982,0.998) 0.142 (0.113,0.175) 4317.055 (199.005,97969.161) 0.041 (0.011,0.103)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.964 (0.926,0.987) 0.997 (0.988,1.000) 0.992 (0.969,0.999) 0.986 (0.971,0.995) 0.274 (0.237,0.314) 340.055 (79.734,5166.400) 0.036 (0.013,0.074)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.968 (0.920,0.992) 0.998 (0.994,1.000) 0.978 (0.935,0.996) 0.997 (0.993,0.999) 0.076 (0.062,0.092) 530.143 (169.366,2932.296) 0.032 (0.008,0.080)
CXR-CAD --> LAM
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.717 (0.681,0.751) 0.989 (0.986,0.991) 0.890 (0.861,0.915) 0.965 (0.960,0.970) 0.091 (0.084,0.099) 63.574 (49.718,83.490) 0.286 (0.252,0.322)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.722 (0.618,0.811) 0.984 (0.974,0.991) 0.793 (0.685,0.875) 0.976 (0.965,0.985) 0.072 (0.057,0.089) 43.980 (26.996,78.645) 0.283 (0.192,0.389)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.719 (0.644,0.785) 0.985 (0.973,0.993) 0.932 (0.878,0.968) 0.926 (0.903,0.945) 0.168 (0.142,0.197) 49.236 (26.414,105.718) 0.285 (0.218,0.362)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.626 (0.499,0.745) 0.984 (0.973,0.992) 0.788 (0.656,0.887) 0.966 (0.951,0.978) 0.068 (0.050,0.088) 40.290 (21.754,81.987) 0.380 (0.259,0.509)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.824 (0.673,0.923) 0.996 (0.990,0.999) 0.875 (0.731,0.957) 0.994 (0.987,0.998) 0.032 (0.022,0.044) 197.429 (83.796,629.181) 0.177 (0.077,0.329)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.770 (0.665,0.856) 0.991 (0.979,0.997) 0.934 (0.854,0.979) 0.961 (0.941,0.977) 0.122 (0.095,0.151) 82.235 (35.685,261.391) 0.232 (0.145,0.339)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.667 (0.588,0.740) 0.983 (0.966,0.993) 0.940 (0.881,0.976) 0.882 (0.848,0.910) 0.200 (0.167,0.237) 39.771 (19.470,101.753) 0.338 (0.265,0.420)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.763 (0.674,0.842) 0.992 (0.985,0.996) 0.887 (0.807,0.942) 0.980 (0.971,0.988) 0.067 (0.053,0.082) 93.897 (51.495,195.945) 0.239 (0.159,0.329)
CRP --> LAM
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.719 (0.683,0.753) 0.996 (0.993,0.997) 0.954 (0.933,0.970) 0.965 (0.960,0.970) 0.085 (0.078,0.093) 162.530 (109.280,253.525) 0.283 (0.248,0.319)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.708 (0.603,0.801) 0.991 (0.984,0.996) 0.875 (0.783,0.942) 0.975 (0.964,0.984) 0.064 (0.050,0.081) 80.679 (43.260,177.973) 0.295 (0.201,0.401)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.777 (0.708,0.838) 1.000 (0.996,1.000) 0.999 (0.984,1.000) 0.942 (0.921,0.959) 0.170 (0.143,0.199) 4276.920 (207.807,81400.835) 0.223 (0.163,0.292)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.712 (0.589,0.812) 0.992 (0.983,0.997) 0.894 (0.788,0.959) 0.974 (0.960,0.984) 0.067 (0.050,0.087) 90.698 (40.903,260.798) 0.290 (0.189,0.415)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.765 (0.607,0.886) 0.996 (0.990,0.999) 0.867 (0.718,0.955) 0.992 (0.985,0.996) 0.030 (0.021,0.042) 184.698 (76.971,586.314) 0.236 (0.114,0.395)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.676 (0.563,0.774) 1.000 (0.995,1.000) 0.998 (0.960,1.000) 0.946 (0.923,0.965) 0.100 (0.076,0.129) 2959.197 (132.241,73368.560) 0.324 (0.226,0.438)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.658 (0.580,0.735) 0.997 (0.988,1.000) 0.989 (0.955,0.999) 0.882 (0.849,0.911) 0.188 (0.155,0.223) 232.808 (53.759,3371.781) 0.343 (0.266,0.421)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.743 (0.646,0.823) 0.996 (0.992,0.999) 0.945 (0.875,0.982) 0.979 (0.969,0.986) 0.061 (0.048,0.075) 206.203 (88.238,647.693) 0.258 (0.178,0.355)
CXR-CAD | CRP --> LAM
Aggregate PHC 0.113 (0.105,0.121) 0.5984 (0.5854,0.6113) 0.792 (0.759,0.822) 0.984 (0.981,0.988) 0.867 (0.837,0.893) 0.974 (0.969,0.978) 0.103 (0.096,0.111) 51.034 (41.043,64.661) 0.212 (0.181,0.245)
Cameroon PHC 0.079 (0.063,0.097) 0.617 (0.5861,0.6472) 0.784 (0.688,0.866) 0.975 (0.964,0.984) 0.730 (0.630,0.817) 0.981 (0.971,0.989) 0.085 (0.069,0.103) 31.394 (21.127,49.171) 0.221 (0.138,0.320)
Nigeria ZRC PHC 0.219 (0.190,0.248) 0.5271 (0.4894,0.5647) 0.811 (0.744,0.866) 0.985 (0.973,0.993) 0.939 (0.890,0.971) 0.949 (0.929,0.965) 0.189 (0.161,0.217) 55.236 (29.268,121.665) 0.192 (0.136,0.261)
Kenya PHC 0.084 (0.066,0.106) 0.6086 (0.5713,0.6449) 0.780 (0.663,0.876) 0.978 (0.965,0.988) 0.768 (0.649,0.861) 0.980 (0.967,0.989) 0.086 (0.066,0.108) 35.297 (21.504,64.086) 0.225 (0.128,0.344)
Bangladesh PHC 0.034 (0.024,0.046) 0.65 (0.6195,0.6796) 0.823 (0.675,0.926) 0.992 (0.985,0.996) 0.778 (0.624,0.893) 0.994 (0.987,0.998) 0.036 (0.026,0.049) 98.973 (51.452,215.248) 0.178 (0.075,0.328)
Brazil Aracaju PHC 0.148 (0.119,0.181) 0.568 (0.5233,0.6119) 0.823 (0.728,0.899) 0.991 (0.978,0.997) 0.938 (0.863,0.980) 0.970 (0.951,0.984) 0.130 (0.103,0.161) 87.324 (37.459,273.068) 0.179 (0.102,0.275)
Brazil Maceio PHC 0.283 (0.247,0.319) 0.494 (0.4493,0.5387) 0.752 (0.678,0.817) 0.980 (0.962,0.991) 0.939 (0.885,0.973) 0.910 (0.878,0.935) 0.225 (0.190,0.264) 38.323 (19.440,87.846) 0.253 (0.187,0.329)
Malawi PHC 0.077 (0.063,0.093) 0.6317 (0.6037,0.659) 0.797 (0.707,0.869) 0.988 (0.981,0.994) 0.852 (0.766,0.914) 0.983 (0.974,0.990) 0.073 (0.058,0.088) 67.549 (40.989,124.022) 0.206 (0.133,0.296)

In figure Figure 1 we show how we plan to summarise sensitivities and specificities (the primary interest of the performance evaluation).

Code
algoNames<-c("Poolxpert.Xpert",
             "Xpert",
             "Lam",
             "Cxr.Poolxpert.Xpert",
             "Crp.Poolxpert.Xpert",
             "Lam.Poolxpert.Xpert",
             "CxrCrpLam.Poolxpert.Xpert",
             "CxrLam.Poolxpert.Xpert",
             "CrpLam.Poolxpert.Xpert",
             "CxrCrp.Poolxpert.Xpert",
             "Cxr.Xpert",
             "Crp.Xpert",
             "Lam.Xpert",
             "CxrCrpLam.Xpert",
             "CxrLam.Xpert",
             "CrpLam.Xpert",
             "CxrCrp.Xpert",
             "Cxr.Lam",
             "Crp.Lam",
             "CxrCrp.Lam"
)

algoLabels<-c("Pooled Xpert Ultra --> Xpert Ultra",
              "Xpert Ultra",
              "LAM",
              "CXR-CAD --> Pooled Xpert Ultra --> Xpert Ultra",
              "CRP --> Pooled Xpert Ultra --> Xpert Ultra",
              "Lam --> Poolxpert --> Xpert",
              "CXR-CAD | CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
              "CXR-CAD | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
              "CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
              "CXR-CAD | CRP --> Pooled Xpert Ultra --> Xpert Ultra",
              "CXR-CAD --> Xpert Ultra",
              "CRP --> Xpert Ultra",
              "LAM --> Xpert Ultra",
              "CXR-CAD | CRP | LAM --> Xpert Ultra",
              "CXR-CAD | LAM --> Xpert Ultra",
              "CRP | LAM --> Xpert Ultra",
              "CXR-CAD | CRP --> Xpert Ultra",
              "CXR-CAD --> LAM",
              "CRP --> LAM",
              "CXR-CAD | CRP --> LAM"
)

settings<-paste(sep="_",dfResPrimBayes$country,dfResPrimBayes$region,dfResPrimBayes$setting)
settings<-settings[grepl(pattern="PHC",settings)]
settings<-settings[c(length(settings),1:(length(settings)-1))]

gr<-expand.grid(settings,algoNames)

dfAlgos<-data.frame(
  setting=gr[,1],
  algo=gr[,2],
  label=algoLabels[match(gr[,2],algoNames)],
  
  sensTxt=NA,
  sensMedian=NA,
  sensLow=NA,
  sensUpp=NA,
  
  specTxt=NA,
  specMedian=NA,
  specLow=NA,
  specUpp=NA,
  
  ppvTxt=NA,
  ppvMedian=NA,
  ppvLow=NA,
  ppvUpp=NA,
  
  npvTxt=NA,
  npvMedian=NA,
  npvLow=NA,
  npvUpp=NA,
  
  dytTxt=NA,
  dytMedian=NA,
  dytLow=NA,
  dytUpp=NA,
  
  dydTxt=NA,
  dydMedian=NA,
  dydLow=NA,
  dydUpp=NA,
  
  lrpTxt=NA,
  lrpMedian=NA,
  lrpLow=NA,
  lrpUpp=NA,
  
  lrmTxt=NA,
  lrmMedian=NA,
  lrmLow=NA,
  lrmUpp=NA
)

for(j in 1:nrow(dfAlgos)){
  res<-allRes[[as.character(dfAlgos$setting[j])]]
  par<-dfAlgos$algo[j]
  
  # sensitivity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","pse.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","pse.",par),c("2.5%","97.5%")])),")")
  dfAlgos$sensTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$sensMedian[j]<-pointEstimate
  dfAlgos$sensLow[j]<-res$mcmcSummary[paste(sep="","pse.",par),c("2.5%")]
  dfAlgos$sensUpp[j]<-res$mcmcSummary[paste(sep="","pse.",par),c("97.5%")]
  
  # specificity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","psp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","psp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$specTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$specMedian[j]<-pointEstimate
  dfAlgos$specLow[j]<-res$mcmcSummary[paste(sep="","psp.",par),c("2.5%")]
  dfAlgos$specUpp[j]<-res$mcmcSummary[paste(sep="","psp.",par),c("97.5%")]
  
  # PPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","ppv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","ppv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$ppvTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$ppvMedian[j]<-pointEstimate
  dfAlgos$ppvLow[j]<-res$mcmcSummary[paste(sep="","ppv.",par),c("2.5%")]
  dfAlgos$ppvUpp[j]<-res$mcmcSummary[paste(sep="","ppv.",par),c("97.5%")]
  
  # NPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","npv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","npv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$npvTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$npvMedian[j]<-pointEstimate
  dfAlgos$npvLow[j]<-res$mcmcSummary[paste(sep="","npv.",par),c("2.5%")]
  dfAlgos$npvUpp[j]<-res$mcmcSummary[paste(sep="","npv.",par),c("97.5%")]
  
  # DYT
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","dyt.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","dyt.",par),c("2.5%","97.5%")])),")")
  dfAlgos$dytTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$dytMedian[j]<-pointEstimate
  dfAlgos$dytLow[j]<-res$mcmcSummary[paste(sep="","dyt.",par),c("2.5%")]
  dfAlgos$dytUpp[j]<-res$mcmcSummary[paste(sep="","dyt.",par),c("97.5%")]
  
  # LR+
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrpTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$lrpMedian[j]<-pointEstimate
  dfAlgos$lrpLow[j]<-res$mcmcSummary[paste(sep="","lrp.",par),c("2.5%")]
  dfAlgos$lrpUpp[j]<-res$mcmcSummary[paste(sep="","lrp.",par),c("97.5%")]
  
  # LR-
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrm.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrm.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrmTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$lrmMedian[j]<-pointEstimate
  dfAlgos$lrmLow[j]<-res$mcmcSummary[paste(sep="","lrm.",par),c("2.5%")]
  dfAlgos$lrmUpp[j]<-res$mcmcSummary[paste(sep="","lrm.",par),c("97.5%")]
}

uniqueAlgos<-unique(dfAlgos$label)

for(i in 1:length(uniqueAlgos)){
  if(i==1){
    dt<-c(uniqueAlgos[i],rep("",10))
    names(dt)<-c("Setting","sensCI","sensEst","sensLow","sensHigh","Sensitivity","specCI","specEst","specLow","specHigh","Specificity")
  }else{
    dt<-rbind(dt,c(uniqueAlgos[i],rep("",10)))
  }
  dfAlgosTmp<-dfAlgos %>% dplyr::filter(label==uniqueAlgos[i])
  dtTmp<-cbind(gsub(paste(sep="","   ",dfAlgosTmp$setting),pattern="_+",replacement=" "),
               dfAlgosTmp$sensTxt,dfAlgosTmp$sensMedian,dfAlgosTmp$sensLow,dfAlgosTmp$sensUpp,paste(rep(" ", 20), collapse = " "),
               dfAlgosTmp$specTxt,dfAlgosTmp$specMedian,dfAlgosTmp$specLow,dfAlgosTmp$specUpp,paste(rep(" ", 20), collapse = " "))
  colnames(dtTmp)<-c("Setting","sensCI","sensEst","sensLow","sensHigh","Sensitivity","specCI","specEst","specLow","specHigh","Specificity")
  dt<-rbind(dt,dtTmp)
  rm(dtTmp,dfAlgosTmp)
}

dt<-as.data.frame(dt)
dt$sensEst<-as.double(dt$sensEst)
dt$sensLow<-as.double(dt$sensLow)
dt$sensHigh<-as.double(dt$sensHigh)
dt$specEst<-as.double(dt$specEst)
dt$specLow<-as.double(dt$specLow)
dt$specHigh<-as.double(dt$specHigh)

dt$sensCI[dt$sensCI=="NA"]<-""
dt$specCI[dt$specCI=="NA"]<-""

# Set-up theme
tm <- forest_theme(base_size = 10,
                   refline_gp = gpar(lty="blank"),
                   ci_pch = c(15),
                   ci_col = c("steelblue"),
                   footnote_gp = gpar(col = "darkgreen",fontsize=8),
                   vertline_lty = c("dashed", "dashed"),
                   vertline_col = c("darkred","orange"),
                   # Table cell padding, width 4 and heights 3
                   core = list(padding = unit(c(4, 3), "mm")))

p <- forest(dt[,c("Setting","sensCI","Sensitivity","specCI","Specificity")],
            est = list(dt$sensEst,dt$specEst),
            lower = list(dt$sensLow,dt$specLow),
            upper = list(dt$sensHigh,dt$specHigh),
            ci_column = c(3, 5),
            ref_line = 1,
            vert_line = list(c(0.75,0.85),c(0.90,0.95)),
            nudge_y = 0,
            theme = tm,
            xlim=c(0,1),
            footnote="The dashed lines indicate minimum (red) and optimum (orange) thresholds.")

plot(p)

pdf(paste(sep="",outPrefix,"S4A_mcmcPars_forestPlot_PcfIcfPHC_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),width=11,height=40)
plot(p)
dev.off()
quartz_off_screen 
                2 
Figure 1: Facility-based evaluation (all participants): forest plots showing the Bayesian posterior estimates for sensitivity and specificity for PHC / lowest level of primary care settings.

5.5.1.3 Facility-based district level

The PCF/ICF district-level results (which will be for the exact same algorithms) will be presented in the same way as the results for the lowest level of care setting (but obviously slight variation in countries for which data are available).

5.5.1.4 Community-based, W4SS entry

The community-based W4SS district-level results (which will be for the exact same algorithms) will be presented in the same way as the results for the lowest level of care setting (but obviously slight variation in countries for which data are available as well as multiple settings that use community-based screening).

5.5.1.5 ACF CXR-CAD entry

These will be presented similarly, though involve a different set of evaluated algorithms (see Section 5.2.4).

Code
algoNames<-c("Poolxpert.Xpert",
             "Xpert",
             "Lam",
             "Crp.Poolxpert.Xpert",
             "Lam.Poolxpert.Xpert",
             "CrpLam.Poolxpert.Xpert",
             "Crp.Xpert",
             "Lam.Xpert",
             "CrpLam.Xpert",
             "Crp.Lam"
)

algoLabels<-c("Pooled Xpert Ultra --> Xpert Ultra",
             "Xpert Ultra",
             "LAM",
             "CRP --> Pooled Xpert Ultra --> Xpert Ultra",
             "LAM --> Pooled Xpert Ultra --> Xpert Ultra",
             "CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
             "CRP --> Xpert Ultra",
             "LAM --> Xpert Ultra",
             "CRP | LAM --> Xpert Ultra",
             "CRP --> LAM"
)

settings<-paste(sep="_",dfResPrimBayes$country,dfResPrimBayes$region,dfResPrimBayes$setting)
settings<-settings[dfResPrimBayes$entryToStudy=="CXR"]
settings<-settings[c(length(settings),1:(length(settings)-1))]

gr<-expand.grid(settings,algoNames)

dfAlgos<-data.frame(
  setting=gr[,1],
  algo=gr[,2],
  label=algoLabels[match(gr[,2],algoNames)],
  prev=NA,
  prevAS=NA,
  sens=NA,
  spec=NA,
  ppv=NA,
  npv=NA,
  dyt=NA,
  lrp=NA,
  lrm=NA
)

for(j in 1:nrow(dfAlgos)){
  res<-allRes[[as.character(dfAlgos$setting[j])]]
  par<-dfAlgos$algo[j]
  
  # prevalence
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary["ptb","50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary["ptb",c("2.5%","97.5%")])),")")
  dfAlgos$prev[j]<-paste(sep=" ",pointEstimate,ci)
  
  # prevalence of asymptomatics
  tmp<-unlist(strsplit(split="_",as.character(dfAlgos$setting[j])))
  if(tmp[1]!="Aggregate"){
    dfTmp<-dfSim %>% filter(country==tmp[1] & region==tmp[2] & setting==tmp[3])
  }else{
    dfTmp<-dfSim %>% filter(setting==tmp[3])
  }
  k<-sum(!dfTmp$w4ss)
  n<-nrow(dfTmp)
  pointEstimate<-round(digits=4,k/n)
  ci<-round(digits=4,binom.test(x=k,n=n)$conf.int)
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",ci)),")")
  dfAlgos$prevAS[j]<-paste(sep=" ",pointEstimate,ci)
  
  # sensitivity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","pse.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","pse.",par),c("2.5%","97.5%")])),")")
  dfAlgos$sens[j]<-paste(sep=" ",pointEstimate,ci)
  
  # specificity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","psp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","psp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$spec[j]<-paste(sep=" ",pointEstimate,ci)
  
  # PPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","ppv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","ppv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$ppv[j]<-paste(sep=" ",pointEstimate,ci)
  
  # NPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","npv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","npv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$npv[j]<-paste(sep=" ",pointEstimate,ci)
  
  # DYT
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","dyt.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","dyt.",par),c("2.5%","97.5%")])),")")
  dfAlgos$dyt[j]<-paste(sep=" ",pointEstimate,ci)
  
  # LR+
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrp[j]<-paste(sep=" ",pointEstimate,ci)
  
  # LR-
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrm.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrm.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrm[j]<-paste(sep=" ",pointEstimate,ci)
}

dfAlgos$setting<-gsub(pattern="_+",replacement=" ",dfAlgos$setting)

colnames(dfAlgos)<-case_when(
  colnames(dfAlgos)=="setting"~"Country & setting",
  colnames(dfAlgos)=="algo"~"Algorithm code",
  colnames(dfAlgos)=="label"~"Algorithm",
  colnames(dfAlgos)=="prev"~"Prevalence",
  colnames(dfAlgos)=="prevAS"~"Prevalence of asymptomatics",
  colnames(dfAlgos)=="sens"~"Sensitivity",
  colnames(dfAlgos)=="spec"~"Specificity",
  colnames(dfAlgos)=="ppv"~"PPV",
  colnames(dfAlgos)=="npv"~"NPV",
  colnames(dfAlgos)=="dyt"~"DYT",
  colnames(dfAlgos)=="dyd"~"DYD",
  colnames(dfAlgos)=="lrp"~"Positive LR",
  colnames(dfAlgos)=="lrm"~"Negative LR"
)

dfAlgos %>%
  dplyr::select(!contains("code") & !contains("Algorithm")) %>%
  kable(caption="Community-based algorithm evaluation (all participants; CXR-CAD entry to study). Asymptomatics proportions are estimated directly, using exact binomial confidence intervals, from the data (not modelled). All other parameters are posterior estimates from the Bayesian modelling approach.") %>%
  kableExtra::kable_styling(full_width = FALSE) %>%
  kableExtra::group_rows(index=table(fct_inorder(dfAlgos$Algorithm)))
Table 11: Community-based evaluation (all participants; CXR entry to study): Bayesian posterior estimates for sensitivity, specificity, positive and negative predictive values and positive and negative likelihood ratios.
Community-based algorithm evaluation (all participants; CXR-CAD entry to study). Asymptomatics proportions are estimated directly, using exact binomial confidence intervals, from the data (not modelled). All other parameters are posterior estimates from the Bayesian modelling approach.
Country & setting Prevalence Prevalence of asymptomatics Sensitivity Specificity PPV NPV DYT Positive LR Negative LR
Pooled Xpert Ultra --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.991 (0.839,1.000) 0.998 (0.993,1.000) 0.916 (0.681,0.993) 1.000 (0.997,1.000) 0.019 (0.010,0.033) 576.950 (139.152,7845.349) 0.009 (0.000,0.161)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.933 (0.810,0.988) 0.997 (0.988,1.000) 0.965 (0.859,0.997) 0.994 (0.982,0.999) 0.078 (0.054,0.109) 313.312 (72.817,4382.851) 0.068 (0.012,0.191)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.864 (0.773,0.927) 0.998 (0.989,1.000) 0.984 (0.932,0.999) 0.976 (0.958,0.988) 0.135 (0.107,0.168) 342.400 (78.618,4518.890) 0.137 (0.073,0.227)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.860 (0.790,0.916) 0.997 (0.989,1.000) 0.990 (0.957,0.999) 0.961 (0.939,0.977) 0.194 (0.161,0.229) 330.071 (75.961,4583.837) 0.141 (0.084,0.211)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.875 (0.766,0.948) 1.000 (0.995,1.000) 0.998 (0.953,1.000) 0.985 (0.971,0.994) 0.093 (0.069,0.122) 3817.505 (167.467,92101.206) 0.125 (0.052,0.234)
Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.995 (0.902,1.000) 0.985 (0.973,0.993) 0.547 (0.331,0.758) 1.000 (0.998,1.000) 0.033 (0.020,0.049) 66.012 (36.271,148.503) 0.005 (0.000,0.100)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.967 (0.869,0.995) 0.984 (0.967,0.994) 0.842 (0.703,0.937) 0.997 (0.988,1.000) 0.093 (0.067,0.124) 60.194 (28.298,154.625) 0.034 (0.005,0.134)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.981 (0.940,0.999) 0.983 (0.968,0.993) 0.915 (0.842,0.963) 0.997 (0.989,1.000) 0.165 (0.135,0.201) 58.131 (31.051,139.398) 0.019 (0.001,0.061)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.980 (0.948,0.995) 0.989 (0.978,0.997) 0.962 (0.927,0.990) 0.994 (0.985,0.998) 0.227 (0.193,0.264) 88.572 (44.833,338.242) 0.021 (0.005,0.052)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.976 (0.917,0.999) 0.984 (0.972,0.993) 0.879 (0.789,0.941) 0.997 (0.990,1.000) 0.118 (0.091,0.148) 61.750 (33.912,131.119) 0.025 (0.001,0.085)
LAM
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.682 (0.427,0.900) 0.922 (0.898,0.941) 0.134 (0.064,0.237) 0.994 (0.986,0.998) 0.090 (0.070,0.114) 8.697 (5.186,13.229) 0.347 (0.108,0.621)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.810 (0.621,0.928) 0.894 (0.857,0.922) 0.399 (0.277,0.523) 0.982 (0.960,0.993) 0.163 (0.129,0.206) 7.486 (5.146,10.779) 0.212 (0.079,0.426)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.840 (0.756,0.908) 0.874 (0.840,0.904) 0.547 (0.456,0.636) 0.968 (0.948,0.982) 0.237 (0.201,0.275) 6.641 (5.086,8.810) 0.183 (0.105,0.277)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.819 (0.744,0.883) 0.898 (0.864,0.924) 0.699 (0.615,0.772) 0.946 (0.921,0.965) 0.263 (0.226,0.302) 8.050 (5.950,10.948) 0.201 (0.130,0.283)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.805 (0.676,0.899) 0.912 (0.884,0.938) 0.521 (0.408,0.631) 0.975 (0.957,0.988) 0.164 (0.133,0.197) 9.141 (6.714,13.205) 0.215 (0.110,0.353)
CRP --> Pooled Xpert Ultra --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.991 (0.832,1.000) 1.000 (0.997,1.000) 0.991 (0.836,1.000) 1.000 (0.997,1.000) 0.018 (0.009,0.030) 6426.745 (304.456,99988.892) 0.009 (0.000,0.168)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.767 (0.599,0.892) 1.000 (0.994,1.000) 0.996 (0.916,1.000) 0.980 (0.962,0.991) 0.062 (0.040,0.089) 2614.637 (125.742,83970.589) 0.233 (0.108,0.401)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.821 (0.726,0.898) 1.000 (0.995,1.000) 0.998 (0.968,1.000) 0.969 (0.949,0.982) 0.127 (0.098,0.159) 3192.808 (158.880,86689.040) 0.179 (0.102,0.275)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.799 (0.717,0.865) 1.000 (0.995,1.000) 0.999 (0.978,1.000) 0.945 (0.921,0.964) 0.179 (0.147,0.212) 3295.229 (158.970,83762.152) 0.201 (0.135,0.283)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.643 (0.504,0.770) 1.000 (0.995,1.000) 0.997 (0.939,1.000) 0.960 (0.938,0.976) 0.068 (0.047,0.094) 2766.412 (128.639,71133.913) 0.357 (0.231,0.497)
LAM --> Pooled Xpert Ultra --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.727 (0.436,0.921) 1.000 (0.997,1.000) 0.987 (0.767,1.000) 0.995 (0.987,0.999) 0.013 (0.006,0.024) 4403.982 (208.190,85872.504) 0.273 (0.079,0.565)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.735 (0.559,0.871) 1.000 (0.994,1.000) 0.996 (0.914,1.000) 0.977 (0.957,0.989) 0.059 (0.038,0.086) 2492.780 (118.878,80960.455) 0.266 (0.129,0.442)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.713 (0.606,0.809) 1.000 (0.995,1.000) 0.998 (0.963,1.000) 0.950 (0.927,0.968) 0.110 (0.084,0.140) 2999.339 (145.153,76918.153) 0.287 (0.192,0.394)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.694 (0.607,0.775) 1.000 (0.995,1.000) 0.999 (0.974,1.000) 0.919 (0.891,0.943) 0.155 (0.125,0.188) 2924.702 (136.899,73903.490) 0.306 (0.225,0.394)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.730 (0.588,0.841) 1.000 (0.995,1.000) 0.997 (0.943,1.000) 0.969 (0.950,0.983) 0.077 (0.055,0.104) 3070.364 (142.939,79524.748) 0.271 (0.159,0.412)
CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.991 (0.840,1.000) 1.000 (0.997,1.000) 0.991 (0.831,1.000) 1.000 (0.997,1.000) 0.018 (0.009,0.030) 6170.866 (293.470,99992.602) 0.009 (0.000,0.160)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.899 (0.764,0.973) 1.000 (0.994,1.000) 0.996 (0.927,1.000) 0.991 (0.978,0.998) 0.073 (0.049,0.102) 3013.227 (150.934,94567.868) 0.101 (0.027,0.236)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.863 (0.773,0.929) 1.000 (0.995,1.000) 0.998 (0.969,1.000) 0.976 (0.958,0.988) 0.133 (0.105,0.165) 3619.938 (170.048,90295.957) 0.137 (0.071,0.228)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.842 (0.767,0.900) 1.000 (0.995,1.000) 0.999 (0.979,1.000) 0.957 (0.934,0.973) 0.188 (0.156,0.223) 3689.659 (171.449,87737.053) 0.158 (0.100,0.234)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.834 (0.713,0.921) 1.000 (0.995,1.000) 0.998 (0.950,1.000) 0.981 (0.965,0.991) 0.088 (0.065,0.116) 3302.761 (171.184,88700.969) 0.166 (0.079,0.288)
CRP --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.991 (0.829,1.000) 0.998 (0.993,1.000) 0.917 (0.692,0.994) 1.000 (0.997,1.000) 0.019 (0.010,0.032) 573.468 (144.300,7879.879) 0.009 (0.000,0.171)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.800 (0.637,0.912) 1.000 (0.994,1.000) 0.996 (0.916,1.000) 0.983 (0.965,0.993) 0.065 (0.043,0.093) 2745.033 (130.401,86393.025) 0.200 (0.088,0.363)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.944 (0.877,0.981) 1.000 (0.995,1.000) 0.999 (0.972,1.000) 0.990 (0.977,0.997) 0.146 (0.115,0.179) 3994.475 (196.091,96946.293) 0.056 (0.019,0.123)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.903 (0.842,0.948) 1.000 (0.995,1.000) 0.999 (0.981,1.000) 0.973 (0.954,0.986) 0.201 (0.168,0.239) 3693.738 (175.389,93111.042) 0.097 (0.052,0.158)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.751 (0.619,0.858) 0.998 (0.990,1.000) 0.973 (0.886,0.998) 0.971 (0.952,0.984) 0.082 (0.059,0.110) 300.930 (70.391,4043.595) 0.250 (0.142,0.382)
LAM --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.728 (0.430,0.921) 0.995 (0.987,0.999) 0.726 (0.443,0.922) 0.995 (0.987,0.999) 0.018 (0.009,0.030) 139.423 (50.084,556.463) 0.273 (0.079,0.573)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.766 (0.597,0.892) 1.000 (0.994,1.000) 0.996 (0.918,1.000) 0.980 (0.962,0.991) 0.062 (0.041,0.090) 2746.956 (132.640,83677.240) 0.234 (0.108,0.403)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.835 (0.740,0.909) 1.000 (0.995,1.000) 0.998 (0.968,1.000) 0.971 (0.952,0.984) 0.129 (0.100,0.161) 3393.846 (159.042,87836.536) 0.165 (0.092,0.261)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.798 (0.719,0.864) 1.000 (0.995,1.000) 0.999 (0.978,1.000) 0.945 (0.921,0.964) 0.178 (0.147,0.213) 3048.093 (156.285,83468.936) 0.202 (0.136,0.281)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.793 (0.664,0.890) 0.995 (0.985,0.999) 0.949 (0.854,0.991) 0.976 (0.958,0.988) 0.088 (0.064,0.117) 159.410 (51.702,879.987) 0.208 (0.110,0.339)
CRP | LAM --> Xpert Ultra
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.991 (0.829,1.000) 0.993 (0.985,0.998) 0.735 (0.484,0.905) 1.000 (0.997,1.000) 0.024 (0.014,0.039) 146.373 (63.345,471.714) 0.009 (0.000,0.172)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.933 (0.810,0.988) 1.000 (0.994,1.000) 0.997 (0.931,1.000) 0.994 (0.982,0.999) 0.076 (0.052,0.105) 3583.084 (159.221,96992.224) 0.067 (0.012,0.190)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.986 (0.943,0.999) 1.000 (0.995,1.000) 0.999 (0.972,1.000) 0.997 (0.989,1.000) 0.152 (0.122,0.186) 4136.783 (195.397,99601.373) 0.014 (0.001,0.057)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.965 (0.921,0.989) 1.000 (0.995,1.000) 0.999 (0.981,1.000) 0.990 (0.977,0.997) 0.216 (0.181,0.253) 4182.457 (186.858,98039.035) 0.035 (0.011,0.079)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.938 (0.848,0.984) 0.993 (0.981,0.998) 0.938 (0.842,0.984) 0.993 (0.981,0.998) 0.106 (0.080,0.136) 125.408 (48.469,493.220) 0.062 (0.016,0.153)
CRP --> LAM
Viet Nam Informal settlements 0.018 (0.010,0.030) 0.6413 (0.6165,0.6656) 0.729 (0.437,0.919) 0.995 (0.987,0.999) 0.728 (0.436,0.922) 0.995 (0.987,0.999) 0.018 (0.009,0.030) 142.385 (49.741,574.906) 0.273 (0.081,0.567)
Cameroon Nomads 0.081 (0.057,0.111) 0.635 (0.6006,0.6684) 0.668 (0.495,0.817) 0.994 (0.982,0.999) 0.908 (0.752,0.983) 0.971 (0.950,0.986) 0.060 (0.038,0.087) 112.524 (35.655,652.339) 0.335 (0.184,0.508)
Nigeria JHF Nomads 0.154 (0.124,0.188) 0.598 (0.5669,0.6286) 0.809 (0.709,0.886) 0.995 (0.985,0.999) 0.967 (0.904,0.994) 0.966 (0.946,0.981) 0.129 (0.100,0.161) 159.399 (52.037,890.788) 0.192 (0.115,0.293)
Nigeria JHF IDP / refugees 0.224 (0.190,0.260) 0.566 (0.5346,0.597) 0.754 (0.669,0.826) 1.000 (0.995,1.000) 0.999 (0.977,1.000) 0.934 (0.907,0.955) 0.169 (0.138,0.202) 3104.305 (148.638,79475.346) 0.246 (0.174,0.331)
Bangladesh Informal settlements 0.106 (0.082,0.135) 0.602 (0.5709,0.6325) 0.624 (0.486,0.749) 1.000 (0.995,1.000) 0.997 (0.938,1.000) 0.958 (0.935,0.974) 0.066 (0.046,0.091) 2519.660 (126.797,69761.472) 0.376 (0.251,0.514)

In figure Figure 2 we show how we plan to summarise sensitivities and specificities (the primary interest of the performance evaluation).

Code
algoNames<-c("Poolxpert.Xpert",
             "Xpert",
             "Lam",
             "Crp.Poolxpert.Xpert",
             "Lam.Poolxpert.Xpert",
             "CrpLam.Poolxpert.Xpert",
             "Crp.Xpert",
             "Lam.Xpert",
             "CrpLam.Xpert",
             "Crp.Lam"
)

algoLabels<-c("Pooled Xpert Ultra --> Xpert Ultra",
             "Xpert Ultra",
             "LAM",
             "CRP --> Pooled Xpert Ultra --> Xpert Ultra",
             "LAM --> Pooled Xpert Ultra --> Xpert Ultra",
             "CRP | LAM --> Pooled Xpert Ultra --> Xpert Ultra",
             "CRP --> Xpert Ultra",
             "LAM --> Xpert Ultra",
             "CRP | LAM --> Xpert Ultra",
             "CRP --> LAM"
)

settings<-paste(sep="_",dfResPrimBayes$country,dfResPrimBayes$region,dfResPrimBayes$setting)
settings<-settings[dfResPrimBayes$entryToStudy=="CXR"]
settings<-settings[c(length(settings),1:(length(settings)-1))]

gr<-expand.grid(settings,algoNames)

dfAlgos<-data.frame(
  setting=gr[,1],
  algo=gr[,2],
  label=algoLabels[match(gr[,2],algoNames)],
  
  sensTxt=NA,
  sensMedian=NA,
  sensLow=NA,
  sensUpp=NA,
  
  specTxt=NA,
  specMedian=NA,
  specLow=NA,
  specUpp=NA,
  
  ppvTxt=NA,
  ppvMedian=NA,
  ppvLow=NA,
  ppvUpp=NA,
  
  npvTxt=NA,
  npvMedian=NA,
  npvLow=NA,
  npvUpp=NA,
  
  dytTxt=NA,
  dytMedian=NA,
  dytLow=NA,
  dytUpp=NA,
  
  dydTxt=NA,
  dydMedian=NA,
  dydLow=NA,
  dydUpp=NA,
  
  lrpTxt=NA,
  lrpMedian=NA,
  lrpLow=NA,
  lrpUpp=NA,
  
  lrmTxt=NA,
  lrmMedian=NA,
  lrmLow=NA,
  lrmUpp=NA
)

for(j in 1:nrow(dfAlgos)){
  res<-allRes[[as.character(dfAlgos$setting[j])]]
  par<-dfAlgos$algo[j]
  
  # sensitivity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","pse.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","pse.",par),c("2.5%","97.5%")])),")")
  dfAlgos$sensTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$sensMedian[j]<-pointEstimate
  dfAlgos$sensLow[j]<-res$mcmcSummary[paste(sep="","pse.",par),c("2.5%")]
  dfAlgos$sensUpp[j]<-res$mcmcSummary[paste(sep="","pse.",par),c("97.5%")]
  
  # specificity
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","psp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","psp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$specTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$specMedian[j]<-pointEstimate
  dfAlgos$specLow[j]<-res$mcmcSummary[paste(sep="","psp.",par),c("2.5%")]
  dfAlgos$specUpp[j]<-res$mcmcSummary[paste(sep="","psp.",par),c("97.5%")]
  
  # PPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","ppv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","ppv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$ppvTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$ppvMedian[j]<-pointEstimate
  dfAlgos$ppvLow[j]<-res$mcmcSummary[paste(sep="","ppv.",par),c("2.5%")]
  dfAlgos$ppvUpp[j]<-res$mcmcSummary[paste(sep="","ppv.",par),c("97.5%")]
  
  # NPV
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","npv.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","npv.",par),c("2.5%","97.5%")])),")")
  dfAlgos$npvTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$npvMedian[j]<-pointEstimate
  dfAlgos$npvLow[j]<-res$mcmcSummary[paste(sep="","npv.",par),c("2.5%")]
  dfAlgos$npvUpp[j]<-res$mcmcSummary[paste(sep="","npv.",par),c("97.5%")]
  
  # DYT
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","dyt.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","dyt.",par),c("2.5%","97.5%")])),")")
  dfAlgos$dytTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$dytMedian[j]<-pointEstimate
  dfAlgos$dytLow[j]<-res$mcmcSummary[paste(sep="","dyt.",par),c("2.5%")]
  dfAlgos$dytUpp[j]<-res$mcmcSummary[paste(sep="","dyt.",par),c("97.5%")]
  
  # LR+
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrp.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrp.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrpTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$lrpMedian[j]<-pointEstimate
  dfAlgos$lrpLow[j]<-res$mcmcSummary[paste(sep="","lrp.",par),c("2.5%")]
  dfAlgos$lrpUpp[j]<-res$mcmcSummary[paste(sep="","lrp.",par),c("97.5%")]
  
  # LR-
  pointEstimate<-gsub(pattern=" ",replacement="",res$mcmcSummary[paste(sep="","lrm.",par),"50%"])
  ci<-paste(sep="","(",gsub(pattern=" ",replacement="",paste(collapse=",",res$mcmcSummary[paste(sep="","lrm.",par),c("2.5%","97.5%")])),")")
  dfAlgos$lrmTxt[j]<-paste(sep=" ",pointEstimate,ci)
  dfAlgos$lrmMedian[j]<-pointEstimate
  dfAlgos$lrmLow[j]<-res$mcmcSummary[paste(sep="","lrm.",par),c("2.5%")]
  dfAlgos$lrmUpp[j]<-res$mcmcSummary[paste(sep="","lrm.",par),c("97.5%")]
}

uniqueAlgos<-unique(dfAlgos$label)

for(i in 1:length(uniqueAlgos)){
  if(i==1){
    dt<-c(uniqueAlgos[i],rep("",10))
    names(dt)<-c("Setting","sensCI","sensEst","sensLow","sensHigh","Sensitivity","specCI","specEst","specLow","specHigh","Specificity")
  }else{
    dt<-rbind(dt,c(uniqueAlgos[i],rep("",10)))
  }
  dfAlgosTmp<-dfAlgos %>% dplyr::filter(label==uniqueAlgos[i])
  dtTmp<-cbind(gsub(paste(sep="","   ",dfAlgosTmp$setting),pattern="_+",replacement=" "),
               dfAlgosTmp$sensTxt,dfAlgosTmp$sensMedian,dfAlgosTmp$sensLow,dfAlgosTmp$sensUpp,paste(rep(" ", 20), collapse = " "),
               dfAlgosTmp$specTxt,dfAlgosTmp$specMedian,dfAlgosTmp$specLow,dfAlgosTmp$specUpp,paste(rep(" ", 20), collapse = " "))
  colnames(dtTmp)<-c("Setting","sensCI","sensEst","sensLow","sensHigh","Sensitivity","specCI","specEst","specLow","specHigh","Specificity")
  dt<-rbind(dt,dtTmp)
  rm(dtTmp,dfAlgosTmp)
}

dt<-as.data.frame(dt)
dt$sensEst<-as.double(dt$sensEst)
dt$sensLow<-as.double(dt$sensLow)
dt$sensHigh<-as.double(dt$sensHigh)
dt$specEst<-as.double(dt$specEst)
dt$specLow<-as.double(dt$specLow)
dt$specHigh<-as.double(dt$specHigh)

dt$sensCI[dt$sensCI=="NA"]<-""
dt$specCI[dt$specCI=="NA"]<-""

# Set-up theme
tm <- forest_theme(base_size = 10,
                   refline_gp = gpar(lty="blank"),
                   ci_pch = c(15),
                   ci_col = c("steelblue"),
                   footnote_gp = gpar(col = "darkgreen",fontsize=8),
                   vertline_lty = c("dashed", "dashed"),
                   vertline_col = c("darkred","orange"),
                   # Table cell padding, width 4 and heights 3
                   core = list(padding = unit(c(4, 3), "mm")))

p <- forest(dt[,c("Setting","sensCI","Sensitivity","specCI","Specificity")],
            est = list(dt$sensEst,dt$specEst),
            lower = list(dt$sensLow,dt$specLow),
            upper = list(dt$sensHigh,dt$specHigh),
            ci_column = c(3, 5),
            ref_line = 1,
            vert_line = list(c(0.75,0.85),c(0.90,0.95)),
            nudge_y = 0,
            theme = tm,
            xlim=c(0,1),
            footnote="The dashed lines indicate minimum (red) and optimum (orange) thresholds.")

plot(p)

pdf(paste(sep="",outPrefix,"S4A_mcmcPars_forestPlot_AcfCxr_",gsub(pattern="-",replacement="",Sys.Date()),".pdf"),width=11,height=14)
plot(p)
dev.off()
quartz_off_screen 
                2 
Figure 2: Community-based evaluation (all participants; CXR-CAD entry to study): forest plots showing the Bayesian posterior estimates for sensitivity and specificity for PHC / lowest level of primary care settings.

5.5.1.6 ACF (W4SS or CXR-CAD) entry

These will be presented similarly as for CXR-CAD only entry to study.

5.5.1.7 Stratified analyses

All of the above analyses will be repeated, stratified by HIV status (PLHIV and participants with unknown or negative HIV status).

6 List of figures

Figure 1: Facility-based evaluation (all participants): forest plots showing the Bayesian posterior estimates for sensitivity and specificity for PHC / lowest level of primary care settings.

Figure 2: Community-based evaluation (all participants; CXR-CAD entry to study): forest plots showing the Bayesian posterior estimates for sensitivity and specificity for PHC / lowest level of primary care settings.

7 List of tables

Table 1: List of abbreviations

Table 2: Version history of the SAP since v1.0.

Table 3: Summary of study populations, countries, and procedures.

Table 4: Summary of diagnostic assays evaluated in this study.

Table 5: Sample size calculation for adults.

Table 6: Sample size for each survey.

Table 7: 10 random rows from the simulated data.

Table 8: Summary of the study data.

Table 9: Summary of the performance of individual tests compared against culture and Xpert Ultra.

Table 10: Facility-based evaluation (all participants): Bayesian posterior estimates for sensitivity, specificity, positive and negative predictive values and positive and negative likelihood ratios for PHC / lowest level of primary care settings.

Table 11: Community-based evaluation (all participants; CXR entry to study): Bayesian posterior estimates for sensitivity, specificity, positive and negative predictive values and positive and negative likelihood ratios.

8 References

Bossuyt, Patrick M, Johannes B Reitsma, David E Bruns, Constantine A Gatsonis, Paul P Glasziou, Les Irwig, Jeroen G Lijmer, et al. 2015. “STARD 2015: An Updated List of Essential Items for Reporting Diagnostic Accuracy Studies.” BMJ, October, h5527. https://doi.org/10.1136/bmj.h5527.
Elm, Erik von, Douglas G. Altman, Matthias Egger, Stuart J. Pocock, Peter C. Gøtzsche, Jan P. Vandenbroucke, and STROBE Initiative. 2007. “The Strengthening the Reporting of Observational Studies in Epidemiology (STROBE) statement: guidelines for reporting observational studies.” Lancet (London, England) 370 (9596): 1453–57. https://doi.org/10.1016/S0140-6736(07)61602-X.
Haldane, J. B. S. 1948. “The Precision of Observed Values of Small Frequencies.” Biometrika 35 (3/4): 297. https://doi.org/10.2307/2332350.
Kerman, Jouni. 2011. “Neutral Noninformative and Informative Conjugate Beta and Gamma Prior Distributions.” Electronic Journal of Statistics 5 (none). https://doi.org/10.1214/11-EJS648.